From 77dd497e9b2131fc2121090d879f6daf5871e4a2 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 30 Jan 2019 09:50:04 -0800 Subject: Remove logspam in InputDispatcher Currently, we are logging an error when a window does not have a registered input channel. But that's not really an error. Convert to a comment instead. Bug: 122436672 Test: presubmit only Change-Id: Ic74a467e67b6a2df8bf46a03f7783d0f4805c429 --- services/inputflinger/InputDispatcher.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 0d5bc15577..7324c28a81 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -3114,17 +3114,29 @@ void InputDispatcher::setInputWindows(const std::vector>& std::vector> newHandles; for (const sp& handle : inputWindowHandles) { - if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr - && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) { - ALOGE("Window handle %s has no registered input channel", - handle->getName().c_str()); + if (!handle->updateInfo()) { + // handle no longer valid + continue; + } + const InputWindowInfo* info = handle->getInfo(); + + if ((getInputChannelLocked(handle->getToken()) == nullptr && + info->portalToDisplayId == ADISPLAY_ID_NONE)) { + const bool noInputChannel = + info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; + const bool canReceiveInput = + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); + if (canReceiveInput && !noInputChannel) { + ALOGE("Window handle %s has no registered input channel", + handle->getName().c_str()); + } continue; } - if (handle->getInfo()->displayId != displayId) { + if (info->displayId != displayId) { ALOGE("Window %s updated by wrong display %d, should belong to display %d", - handle->getName().c_str(), displayId, - handle->getInfo()->displayId); + handle->getName().c_str(), displayId, info->displayId); continue; } -- cgit v1.2.3-59-g8ed1b From b3ad35c924daa36a3d1164dabcb7eea6515589f4 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 5 Apr 2019 10:50:52 -0700 Subject: Refactor setInputWindows The logic for the actual window update is now factored out into a separate function. Otherwise, setInputWindows was too long and tough to reason about. There should be no functional change in this CL. Bug: none Test: atest -a inputflinger_tests Change-Id: I49c2eda5178de79de23f772ffc279249ca4402e8 --- services/inputflinger/InputDispatcher.cpp | 152 +++++++++++++++--------------- services/inputflinger/InputDispatcher.h | 7 ++ 2 files changed, 85 insertions(+), 74 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 7324c28a81..13de53dc20 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -236,9 +236,17 @@ static void dumpRegion(std::string& dump, const Region& region) { } } -template -static T getValueByKey(std::unordered_map& map, U key) { - typename std::unordered_map::const_iterator it = map.find(key); +/** + * Find the entry in std::unordered_map by key, and return it. + * If the entry is not found, return a default constructed entry. + * + * Useful when the entries are vectors, since an empty vector will be returned + * if the entry is not found. + * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. + */ +template +static T getValueByKey(const std::unordered_map& map, U key) { + auto it = map.find(key); return it != map.end() ? it->second : T{}; } @@ -3028,14 +3036,7 @@ void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { std::vector> InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { - std::unordered_map>>::const_iterator it = - mWindowHandlesByDisplay.find(displayId); - if(it != mWindowHandlesByDisplay.end()) { - return it->second; - } - - // Return an empty one if nothing found. - return std::vector>(); + return getValueByKey(mWindowHandlesByDisplay, displayId); } sp InputDispatcher::getWindowHandleLocked( @@ -3077,6 +3078,63 @@ sp InputDispatcher::getInputChannelLocked(const sp& token return mInputChannelsByToken.at(token); } +void InputDispatcher::updateWindowHandlesForDisplayLocked( + const std::vector>& inputWindowHandles, int32_t displayId) { + if (inputWindowHandles.empty()) { + // Remove all handles on a display if there are no windows left. + mWindowHandlesByDisplay.erase(displayId); + return; + } + + // Since we compare the pointer of input window handles across window updates, we need + // to make sure the handle object for the same window stays unchanged across updates. + const std::vector>& oldHandles = getWindowHandlesLocked(displayId); + std::unordered_map, sp, IBinderHash> oldHandlesByTokens; + for (const sp& handle : oldHandles) { + oldHandlesByTokens[handle->getToken()] = handle; + } + + std::vector> newHandles; + for (const sp& handle : inputWindowHandles) { + if (!handle->updateInfo()) { + // handle no longer valid + continue; + } + + const InputWindowInfo* info = handle->getInfo(); + if ((getInputChannelLocked(handle->getToken()) == nullptr && + info->portalToDisplayId == ADISPLAY_ID_NONE)) { + const bool noInputChannel = + info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; + const bool canReceiveInput = + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); + if (canReceiveInput && !noInputChannel) { + ALOGE("Window handle %s has no registered input channel", + handle->getName().c_str()); + } + continue; + } + + if (info->displayId != displayId) { + ALOGE("Window %s updated by wrong display %d, should belong to display %d", + handle->getName().c_str(), displayId, info->displayId); + continue; + } + + if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { + const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); + oldHandle->updateFrom(handle); + newHandles.push_back(oldHandle); + } else { + newHandles.push_back(handle); + } + } + + // Insert or replace + mWindowHandlesByDisplay[displayId] = newHandles; +} + /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... @@ -3096,73 +3154,19 @@ void InputDispatcher::setInputWindows(const std::vector>& const std::vector> oldWindowHandles = getWindowHandlesLocked(displayId); + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + sp newFocusedWindowHandle = nullptr; bool foundHoveredWindow = false; - - if (inputWindowHandles.empty()) { - // Remove all handles on a display if there are no windows left. - mWindowHandlesByDisplay.erase(displayId); - } else { - // Since we compare the pointer of input window handles across window updates, we need - // to make sure the handle object for the same window stays unchanged across updates. - const std::vector>& oldHandles = - mWindowHandlesByDisplay[displayId]; - std::unordered_map, sp, IBinderHash> oldHandlesByTokens; - for (const sp& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; - } - - std::vector> newHandles; - for (const sp& handle : inputWindowHandles) { - if (!handle->updateInfo()) { - // handle no longer valid - continue; - } - const InputWindowInfo* info = handle->getInfo(); - - if ((getInputChannelLocked(handle->getToken()) == nullptr && - info->portalToDisplayId == ADISPLAY_ID_NONE)) { - const bool noInputChannel = - info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; - const bool canReceiveInput = - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); - if (canReceiveInput && !noInputChannel) { - ALOGE("Window handle %s has no registered input channel", - handle->getName().c_str()); - } - continue; - } - - if (info->displayId != displayId) { - ALOGE("Window %s updated by wrong display %d, should belong to display %d", - handle->getName().c_str(), displayId, info->displayId); - continue; - } - - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp oldHandle = - oldHandlesByTokens.at(handle->getToken()); - oldHandle->updateFrom(handle); - newHandles.push_back(oldHandle); - } else { - newHandles.push_back(handle); - } + for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; } - - for (const sp& windowHandle : newHandles) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus - && windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; } - - // Insert or replace - mWindowHandlesByDisplay[displayId] = newHandles; } if (!foundHoveredWindow) { diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 3735a0bcc6..183efa930b 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -1006,6 +1006,13 @@ private: sp getInputChannelLocked(const sp& windowToken) const REQUIRES(mLock); bool hasWindowHandleLocked(const sp& windowHandle) const REQUIRES(mLock); + /* + * Validate and update InputWindowHandles for a given display. + */ + void updateWindowHandlesForDisplayLocked( + const std::vector>& inputWindowHandles, int32_t displayId) + REQUIRES(mLock); + // Focus tracking for keys, trackball, etc. std::unordered_map> mFocusedWindowHandlesByDisplay GUARDED_BY(mLock); -- cgit v1.2.3-59-g8ed1b From 4180aa424305e7fc9ffd66daddb50b3ec3ddaf01 Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Fri, 10 May 2019 16:27:48 -0700 Subject: Remove mUserEpollWakeup from EventHub All devices supporting Oreo must have a linux kernel version 3.18 or above, meaning it isn't necessary to check if the kernel version is above 3.5. Bug: 118001226 Test: After compiling and creating a build and installing on device, there were no crashes. Change-Id: I74f04524199b9220d836c91f8e71e85fe75ba173 --- services/inputflinger/EventHub.cpp | 31 +------------------------------ services/inputflinger/EventHub.h | 2 -- 2 files changed, 1 insertion(+), 32 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index ce5627271a..af023148cc 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #define LOG_TAG "EventHub" @@ -94,14 +93,6 @@ static std::string sha1(const std::string& in) { return out; } -static void getLinuxRelease(int* major, int* minor) { - struct utsname info; - if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) { - *major = 0, *minor = 0; - ALOGE("Could not get linux version: %s", strerror(errno)); - } -} - /** * Return true if name matches "v4l-touch*" */ @@ -292,11 +283,6 @@ EventHub::EventHub(void) : result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); - - int major, minor; - getLinuxRelease(&major, &minor); - // EPOLLWAKEUP was introduced in kernel 3.5 - mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5); } EventHub::~EventHub(void) { @@ -1487,28 +1473,13 @@ void EventHub::configureFd(Device* device) { } } - std::string wakeMechanism = "EPOLLWAKEUP"; - if (!mUsingEpollWakeup) { -#ifndef EVIOCSSUSPENDBLOCK - // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels - // will use an epoll flag instead, so as long as we want to support - // this feature, we need to be prepared to define the ioctl ourselves. -#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) -#endif - if (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) { - wakeMechanism = ""; - } else { - wakeMechanism = "EVIOCSSUSPENDBLOCK"; - } - } // Tell the kernel that we want to use the monotonic clock for reporting timestamps // associated with input events. This is important because the input system // uses the timestamps extensively and assumes they were recorded using the monotonic // clock. int clockId = CLOCK_MONOTONIC; bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), - toString(usingClockIoctl)); + ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); } void EventHub::openVideoDeviceLocked(const std::string& devicePath) { diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h index 63a20ef3e2..eb4e8f2e50 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/EventHub.h @@ -479,8 +479,6 @@ private: size_t mPendingEventCount; size_t mPendingEventIndex; bool mPendingINotify; - - bool mUsingEpollWakeup; }; }; // namespace android -- cgit v1.2.3-59-g8ed1b From 9b5a821d1e214cac16a7d03b590964375fd2614a Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 1 Jul 2019 14:25:40 -0700 Subject: Do not use moved-from object We are currently reading a field from a moved-from object. Remove this read. Bug: none Test: none Change-Id: I0d33636f653b7f7ae0b98e4645ee8779bf9da2ea --- services/inputflinger/InputClassifier.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 6a7f2797f4..7c061c5857 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -276,7 +276,7 @@ void MotionClassifier::enqueueEvent(ClassifierEvent&& event) { bool eventAdded = mEvents.push(std::move(event)); if (!eventAdded) { // If the queue is full, suspect the HAL is slow in processing the events. - ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime); + ALOGE("Could not add the event to the queue. Resetting"); reset(); } } -- cgit v1.2.3-59-g8ed1b From 00f511d329924824b1961e9472c3a06683fc2216 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 12 Jun 2019 16:55:40 -0700 Subject: Dispatch mouse events to window under the cursor. This CL adds cursor positions to NotifyMotionArgs, MotionEntry, InputMessage motion body and MotionEvent. Bug: 134788085 Test: The window under the cursor always responds to the gesture. Test: atest inputflinger_tests Test: atest libinput_tests Change-Id: I8ea460ed8738ffc3a5e997215685889cc1e1f2fe --- include/input/Input.h | 48 ++-- include/input/InputTransport.h | 31 +-- libs/input/Input.cpp | 55 +++-- libs/input/InputTransport.cpp | 62 ++---- libs/input/tests/InputEvent_test.cpp | 52 ++++- .../input/tests/InputPublisherAndConsumer_test.cpp | 40 ++-- libs/input/tests/StructLayout_test.cpp | 6 +- libs/input/tests/VelocityTracker_test.cpp | 13 +- services/inputflinger/InputDispatcher.cpp | 240 +++++++++++--------- services/inputflinger/InputDispatcher.h | 20 +- services/inputflinger/InputListener.cpp | 100 +++++---- services/inputflinger/InputReader.cpp | 248 +++++++++++---------- services/inputflinger/include/InputListener.h | 22 +- .../tests/InputClassifierConverter_test.cpp | 15 +- .../inputflinger/tests/InputClassifier_test.cpp | 15 +- .../inputflinger/tests/InputDispatcher_test.cpp | 143 ++++++++---- 16 files changed, 650 insertions(+), 460 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index 805957a5ca..a97624658c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,12 +24,14 @@ */ #include +#include #include #include #include #include #include -#include + +#include /* * Additional private constants not defined in ndk/ui/input.h. @@ -246,6 +248,13 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +/** + * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't + * use it for direct comparison with any other value, because NaN isn't equal to itself according to + * IEEE 754. Use isnan() instead to check if a cursor position is valid. + */ +constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); + /* * Pointer coordinate data. */ @@ -459,6 +468,14 @@ public: inline float getYPrecision() const { return mYPrecision; } + inline float getRawXCursorPosition() const { return mXCursorPosition; } + + float getXCursorPosition() const; + + inline float getRawYCursorPosition() const { return mYCursorPosition; } + + float getYCursorPosition() const; + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } @@ -600,26 +617,13 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, + float yOffset, float xPrecision, float yPrecision, float mXCursorPosition, + float mYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -669,6 +673,8 @@ protected: float mYOffset; float mXPrecision; float mYPrecision; + float mXCursorPosition; + float mYCursorPosition; nsecs_t mDownTime; Vector mPointerProperties; Vector mSampleEventTimes; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 63606e5911..df23f613c8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -113,6 +113,8 @@ struct InputMessage { float yOffset; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; uint32_t pointerCount; uint32_t empty3; // Note that PointerCoords requires 8 byte alignment. @@ -261,27 +263,14 @@ public: * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ - status_t publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 9fd25f9cb7..3266b0740d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -235,26 +235,14 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; @@ -267,6 +255,8 @@ void MotionEvent::initialize( mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; + mXCursorPosition = xCursorPosition; + mYCursorPosition = yCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -288,6 +278,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; + mXCursorPosition = other->mXCursorPosition; + mYCursorPosition = other->mYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -312,6 +304,16 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +float MotionEvent::getXCursorPosition() const { + const float rawX = getRawXCursorPosition(); + return rawX + mXOffset; +} + +float MotionEvent::getYCursorPosition() const { + const float rawY = getRawYCursorPosition(); + return rawY + mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -431,6 +433,15 @@ void MotionEvent::transform(const float matrix[9]) { float originX, originY; transformPoint(matrix, 0, 0, &originX, &originY); + // Apply the transformation to cursor position. + if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + float x = mXCursorPosition + oldXOffset; + float y = mYCursorPosition + oldYOffset; + transformPoint(matrix, x, y, &x, &y); + mXCursorPosition = x - mXOffset; + mYCursorPosition = y - mYOffset; + } + // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { @@ -470,6 +481,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); + mXCursorPosition = parcel->readFloat(); + mYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -521,6 +534,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); + parcel->writeFloat(mXCursorPosition); + parcel->writeFloat(mYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d02cb8ea46..904a6feb03 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -191,6 +191,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision msg->body.motion.yPrecision = body.motion.yPrecision; + // float xCursorPosition + msg->body.motion.xCursorPosition = body.motion.xCursorPosition; + // float yCursorPosition + msg->body.motion.yCursorPosition = body.motion.yCursorPosition; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; //struct Pointer pointers[MAX_POINTERS] @@ -465,26 +469,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -532,6 +522,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; + msg.body.motion.xCursorPosition = xCursorPosition; + msg.body.motion.yCursorPosition = yCursorPosition; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -1135,26 +1127,16 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize( - msg->body.motion.deviceId, - msg->body.motion.source, - msg->body.motion.displayId, - msg->body.motion.action, - msg->body.motion.actionButton, - msg->body.motion.flags, - msg->body.motion.edgeFlags, - msg->body.motion.metaState, - msg->body.motion.buttonState, - msg->body.motion.classification, - msg->body.motion.xOffset, - msg->body.motion.yOffset, - msg->body.motion.xPrecision, - msg->body.motion.yPrecision, - msg->body.motion.downTime, - msg->body.motion.eventTime, - pointerCount, - pointerProperties, - pointerCoords); + event->initialize(msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.actionButton, msg->body.motion.flags, + msg->body.motion.edgeFlags, msg->body.motion.metaState, + msg->body.motion.buttonState, msg->body.motion.classification, + msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 2b75c82bb1..ec34f3e652 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -255,11 +255,11 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, - 2, pointerProperties, pointerCoords); + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, + AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, + ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -571,10 +571,11 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, + 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, + 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -602,6 +603,10 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } + // Check cursor positions. + ASSERT_NEAR(sinf(PI_180 * (90 + ROTATION)) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(-cosf(PI_180 * (90 + ROTATION)) * RADIUS, event.getYCursorPosition(), 0.001); + // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); @@ -626,11 +631,34 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { for (MotionClassification classification : classifications) { event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } } +TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, + 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, + 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + event.offsetLocation(20, 60); + ASSERT_EQ(280, event.getRawXCursorPosition()); + ASSERT_EQ(540, event.getRawYCursorPosition()); + ASSERT_EQ(300, event.getXCursorPosition()); + ASSERT_EQ(600, event.getYCursorPosition()); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index f2cd1be33f..a362f3281d 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -146,6 +146,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yOffset = -20; constexpr float xPrecision = 0.25; constexpr float yPrecision = 0.5; + constexpr float xCursorPosition = 1.3; + constexpr float yCursorPosition = 50.6; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -168,10 +170,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, + flags, edgeFlags, metaState, buttonState, classification, + xOffset, yOffset, xPrecision, yPrecision, + xCursorPosition, yCursorPosition, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -199,6 +203,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(classification, motionEvent->getClassification()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); + EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); + EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition()); + EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -266,9 +274,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -279,9 +289,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -297,9 +309,11 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 62023fb328..8d8cf06c91 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -64,8 +64,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 368446ff4d..968e2fa6bc 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,12 +176,13 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - action, 0 /*actionButton*/, 0 /*flags*/, - AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, + entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); } diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index c2ff4c9629..b32309990f 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -262,14 +262,18 @@ static T getValueByKey(std::unordered_map& map, U key) { // --- InputDispatcher --- -InputDispatcher::InputDispatcher(const sp& policy) : - mPolicy(policy), - mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED), - mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), - mNextUnblockedEvent(nullptr), - mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { +InputDispatcher::InputDispatcher(const sp& policy) + : mPolicy(policy), + mPendingEvent(nullptr), + mLastDropReason(DROP_REASON_NOT_DROPPED), + mAppSwitchSawKeyDown(false), + mAppSwitchDueTime(LONG_LONG_MAX), + mNextUnblockedEvent(nullptr), + mDispatchEnabled(false), + mDispatchFrozen(false), + mInputFilterEnabled(false), + mFocusedDisplayId(ADISPLAY_ID_DEFAULT), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -1326,6 +1330,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); + const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; @@ -1361,11 +1366,17 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ + int32_t x; + int32_t y; int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_Y)); + // Always dispatch mouse events to cursor position. + if (isFromMouse) { + x = int32_t(entry->xCursorPosition); + y = int32_t(entry->yCursorPosition); + } else { + x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; sp newTouchedWindowHandle = findTouchedWindowAtLocked( displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); @@ -2256,15 +2267,21 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Publish the motion event. - status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, - motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, motionEntry->actionButton, - dispatchEntry->resolvedFlags, motionEntry->edgeFlags, - motionEntry->metaState, motionEntry->buttonState, motionEntry->classification, - xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->downTime, motionEntry->eventTime, - motionEntry->pointerCount, motionEntry->pointerProperties, - usingCoords); + status = + connection->inputPublisher + .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, + motionEntry->source, motionEntry->displayId, + dispatchEntry->resolvedAction, + motionEntry->actionButton, + dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, + motionEntry->buttonState, + motionEntry->classification, xOffset, yOffset, + motionEntry->xPrecision, motionEntry->yPrecision, + motionEntry->xCursorPosition, + motionEntry->yCursorPosition, motionEntry->downTime, + motionEntry->eventTime, motionEntry->pointerCount, + motionEntry->pointerProperties, usingCoords); break; } @@ -2590,24 +2607,17 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet } } - MotionEntry* splitMotionEntry = new MotionEntry( - originalMotionEntry->sequenceNum, - originalMotionEntry->eventTime, - originalMotionEntry->deviceId, - originalMotionEntry->source, - originalMotionEntry->displayId, - originalMotionEntry->policyFlags, - action, - originalMotionEntry->actionButton, - originalMotionEntry->flags, - originalMotionEntry->metaState, - originalMotionEntry->buttonState, - originalMotionEntry->classification, - originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, - originalMotionEntry->yPrecision, - originalMotionEntry->downTime, - splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); + MotionEntry* splitMotionEntry = + new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, + originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, + action, originalMotionEntry->actionButton, originalMotionEntry->flags, + originalMotionEntry->metaState, originalMotionEntry->buttonState, + originalMotionEntry->classification, originalMotionEntry->edgeFlags, + originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, + originalMotionEntry->xCursorPosition, + originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, + splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { splitMotionEntry->injectionState = originalMotionEntry->injectionState; @@ -2753,12 +2763,14 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime); + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " + "mYCursorPosition=%f, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->actionButton, args->flags, args->metaState, args->buttonState, + args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, + args->yCursorPosition, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -2800,12 +2812,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, - args->action, args->actionButton, - args->flags, args->edgeFlags, args->metaState, args->buttonState, - args->classification, 0, 0, args->xPrecision, args->yPrecision, - args->downTime, args->eventTime, - args->pointerCount, args->pointerProperties, args->pointerCoords); + event.initialize(args->deviceId, args->source, args->displayId, args->action, + args->actionButton, args->flags, args->edgeFlags, args->metaState, + args->buttonState, args->classification, 0, 0, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -2816,12 +2828,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } // Just enqueue a new motion event. - MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime, - args->deviceId, args->source, args->displayId, policyFlags, - args->action, args->actionButton, args->flags, - args->metaState, args->buttonState, args->classification, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, - args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + MotionEntry* newEntry = + new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, args->actionButton, + args->flags, args->metaState, args->buttonState, + args->classification, args->edgeFlags, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->pointerCount, args->pointerProperties, + args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); @@ -2952,31 +2966,34 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), - policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); + firstInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry = firstInjectedEntry; for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; - MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, - *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); + MotionEntry* nextInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry->next = nextInjectedEntry; lastInjectedEntry = nextInjectedEntry; } @@ -4633,21 +4650,32 @@ void InputDispatcher::KeyEntry::recycle() { // --- InputDispatcher::MotionEntry --- -InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, +InputDispatcher::MotionEntry::MotionEntry( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, - uint32_t pointerCount, + int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset) : - EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + float xOffset, float yOffset) + : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), eventTime(eventTime), - deviceId(deviceId), source(source), displayId(displayId), action(action), - actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), - xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), pointerCount(pointerCount) { + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), + pointerCount(pointerCount) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); @@ -4662,11 +4690,14 @@ InputDispatcher::MotionEntry::~MotionEntry() { void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 - ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, " - "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags, - metaState, buttonState, motionClassificationToString(classification), edgeFlags, - xPrecision, yPrecision); + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " + "buttonState=0x%08x, " + "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " + "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), + actionButton, flags, metaState, buttonState, + motionClassificationToString(classification), edgeFlags, xPrecision, + yPrecision, xCursorPosition, yCursorPosition); for (uint32_t i = 0; i < pointerCount; i++) { if (i) { @@ -4936,6 +4967,8 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, memento.flags = flags; memento.xPrecision = entry->xPrecision; memento.yPrecision = entry->yPrecision; + memento.xCursorPosition = entry->xCursorPosition; + memento.yCursorPosition = entry->yCursorPosition; memento.downTime = entry->downTime; memento.setPointers(entry); memento.hovering = hovering; @@ -4966,13 +4999,16 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, memento.policyFlags, - action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - memento.xPrecision, memento.yPrecision, memento.downTime, - memento.pointerCount, memento.pointerProperties, memento.pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + outEvents.push_back( + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, memento.pointerCount, + memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); } } } diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 753b748884..46dd9bd273 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -570,19 +570,21 @@ private: int32_t edgeFlags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); virtual void appendDescription(std::string& msg) const; protected: @@ -830,6 +832,8 @@ private: int32_t flags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 423b69cff3..0498e87732 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -21,6 +21,7 @@ #include "InputListener.h" #include +#include namespace android { @@ -87,21 +88,33 @@ void NotifyKeyArgs::notify(const sp& listener) const { // --- NotifyMotionArgs --- -NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, MotionClassification classification, +NotifyMotionArgs::NotifyMotionArgs( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector& videoFrames) : - NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source), - displayId(displayId), policyFlags(policyFlags), - action(action), actionButton(actionButton), - flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp), + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, const std::vector& videoFrames) + : NotifyArgs(sequenceNum, eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + deviceTimestamp(deviceTimestamp), pointerCount(pointerCount), - xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), videoFrames(videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); @@ -109,14 +122,26 @@ NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int3 } } -NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId), - source(other.source), displayId(other.displayId), policyFlags(other.policyFlags), - action(other.action), actionButton(other.actionButton), flags(other.flags), - metaState(other.metaState), buttonState(other.buttonState), - classification(other.classification), edgeFlags(other.edgeFlags), - deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount), - xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime), +NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) + : NotifyArgs(other.sequenceNum, other.eventTime), + deviceId(other.deviceId), + source(other.source), + displayId(other.displayId), + policyFlags(other.policyFlags), + action(other.action), + actionButton(other.actionButton), + flags(other.flags), + metaState(other.metaState), + buttonState(other.buttonState), + classification(other.classification), + edgeFlags(other.edgeFlags), + deviceTimestamp(other.deviceTimestamp), + pointerCount(other.pointerCount), + xPrecision(other.xPrecision), + yPrecision(other.yPrecision), + xCursorPosition(other.xCursorPosition), + yCursorPosition(other.yCursorPosition), + downTime(other.downTime), videoFrames(other.videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(other.pointerProperties[i]); @@ -124,28 +149,23 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : } } +static inline bool isCursorPositionEqual(float lhs, float rhs) { + return (isnan(lhs) && isnan(rhs)) || lhs == rhs; +} + bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { - bool equal = - sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && deviceId == rhs.deviceId - && source == rhs.source - && displayId == rhs.displayId - && policyFlags == rhs.policyFlags - && action == rhs.action - && actionButton == rhs.actionButton - && flags == rhs.flags - && metaState == rhs.metaState - && buttonState == rhs.buttonState - && classification == rhs.classification - && edgeFlags == rhs.edgeFlags - && deviceTimestamp == rhs.deviceTimestamp - && pointerCount == rhs.pointerCount + bool equal = sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime && + deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && + policyFlags == rhs.policyFlags && action == rhs.action && + actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && + buttonState == rhs.buttonState && classification == rhs.classification && + edgeFlags == rhs.edgeFlags && deviceTimestamp == rhs.deviceTimestamp && + pointerCount == rhs.pointerCount // PointerProperties and PointerCoords are compared separately below - && xPrecision == rhs.xPrecision - && yPrecision == rhs.yPrecision - && downTime == rhs.downTime - && videoFrames == rhs.videoFrames; + && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && + isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) && + isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) && + downTime == rhs.downTime && videoFrames == rhs.videoFrames; if (!equal) { return false; } diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index a45b8a56ce..9e5990964c 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -2782,6 +2782,8 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerVelocityControl.move(when, &deltaX, &deltaY); int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { if (moved || scrolled || buttonsChanged) { mPointerController->setPresentation( @@ -2798,10 +2800,9 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); } - float x, y; - mPointerController->getPosition(&x, &y); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); displayId = mPointerController->getDisplayId(); @@ -2845,21 +2846,25 @@ void CursorInputMapper::sync(nsecs_t when) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&releaseArgs); } } NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); if (buttonsPressed) { @@ -2868,11 +2873,14 @@ void CursorInputMapper::sync(nsecs_t when) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, - actionButton, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&pressArgs); } } @@ -2882,12 +2890,14 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&hoverArgs); } @@ -2897,11 +2907,14 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } } @@ -3041,12 +3054,13 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = mContext->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, 0, /* videoFrames */ {}); + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } @@ -5450,12 +5464,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, mPointerGesture.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + x, y, mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6360,29 +6374,31 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); int32_t displayId = mViewport.displayId; - if (mPointerController != nullptr) { - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6390,13 +6406,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6406,25 +6422,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6433,26 +6449,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6468,13 +6483,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &pointerCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6531,16 +6546,21 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 ALOG_ASSERT(false); } } + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, - source, displayId, policyFlags, - action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, - edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, - xPrecision, yPrecision, downTime, std::move(frames)); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount, + pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, + yCursorPosition, downTime, std::move(frames)); getListener()->notifyMotion(&args); } @@ -7462,10 +7482,12 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { uint32_t policyFlags = 0; NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &pointerProperties, &pointerCoords, 0, 0, 0, /* videoFrames */ {}); + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&args); } diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index b51dcb6cad..57c894bf8b 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -119,19 +119,27 @@ struct NotifyMotionArgs : public NotifyArgs { PointerCoords pointerCoords[MAX_POINTERS]; float xPrecision; float yPrecision; + /** + * Mouse cursor position when this event is reported relative to the origin of the specified + * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in + * gestures enabled mode. + */ + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; std::vector videoFrames; inline NotifyMotionArgs() { } NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector& videoFrames); + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, + uint32_t deviceTimestamp, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, + const std::vector& videoFrames); NotifyMotionArgs(const NotifyMotionArgs& other); diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp index 813b69edbb..ba1c7c9284 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2); coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, + 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 7cc17a2215..9bc4282d9c 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, + 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9fe6481ca0..c28a6214d6 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -249,8 +249,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects undefined motion actions. event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -258,18 +260,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer down with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -277,36 +285,45 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer up with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -314,18 +331,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -334,9 +355,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -463,6 +486,7 @@ public: mInfo.frameRight = mFrame.right; mInfo.frameBottom = mFrame.bottom; mInfo.globalScaleFactor = 1.0; + mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.visible = true; mInfo.canReceiveKeys = true; @@ -521,8 +545,10 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionDown(const sp& dispatcher, int32_t source, - int32_t displayId, int32_t x = 100, int32_t y = 200) { +static int32_t injectMotionEvent(const sp& dispatcher, int32_t action, + int32_t source, int32_t displayId, int32_t x, int32_t y, + int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, + int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -537,12 +563,11 @@ static int32_t injectMotionDown(const sp& dispatcher, int32_t s nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -551,6 +576,11 @@ static int32_t injectMotionDown(const sp& dispatcher, int32_t s INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } +static int32_t injectMotionDown(const sp& dispatcher, int32_t source, + int32_t displayId, int32_t x = 100, int32_t y = 200) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y); +} + static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. @@ -576,11 +606,12 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties, - pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime, - /* videoFrames */ {}); + POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, + AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); return args; } @@ -704,6 +735,32 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); } +TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { + sp application = new FakeApplicationHandle(); + + sp windowLeft = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + windowLeft->setFrame(Rect(0, 0, 600, 800)); + windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + sp windowRight = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + windowRight->setFrame(Rect(600, 0, 1200, 800)); + windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + std::vector> inputWindowHandles{windowLeft, windowRight}; + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + + // Inject an event with coordinate in the area of right window, with mouse cursor in the area of + // left window. This event should be dispatched to the left window. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowRight->assertNoEvents(); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: -- cgit v1.2.3-59-g8ed1b From addb02b4146f119892499510255cd3df247db14c Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Tue, 25 Jun 2019 16:36:13 -0700 Subject: Stop splitting mouse events. Mouse events should always be dispatched to the window under the cursor, so it shouldn't be split in any case. This change's parent already uses mouse cursor position to dispatch mouse events regardless of the coordinate of events. Therefore this change is just to make it conceptually correct. Bug: 134788085 Test: The window under the cursor always responds to the gesture. Change-Id: Id0d791bb75df7be8ba5c49e840b5655c008fded0 --- services/inputflinger/InputDispatcher.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index b32309990f..be1370747c 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -1388,8 +1388,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - // New window supports splitting. - isSplit = true; + // New window supports splitting, but we should never split mouse events. + isSplit = !isFromMouse; } else if (isSplit) { // New window does not support splitting but we have already split events. // Ignore the new window. -- cgit v1.2.3-59-g8ed1b From 21da0ffff1424d2792564e785c981d3978c6450e Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Fri, 28 Jun 2019 13:22:51 -0700 Subject: Remove handling of EV_MSC/MSC_TIMESTAMP in Input MSC_TIMESTAMP is not being utilized anymore to report timestamps from drivers. MSC_TIMESTAMP and all other utilizations of it are removed. Bug: 119840121 Test: No crashes happening after building and running on flame Change-Id: I4d9bec3d763bee3583c351b4240f145a21711e33 --- services/inputflinger/InputClassifierConverter.cpp | 2 +- services/inputflinger/InputListener.cpp | 12 +- services/inputflinger/InputReader.cpp | 188 +++++++++------------ services/inputflinger/InputReader.h | 9 - services/inputflinger/include/InputListener.h | 11 +- .../tests/InputClassifierConverter_test.cpp | 6 +- .../inputflinger/tests/InputClassifier_test.cpp | 6 +- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- services/inputflinger/tests/InputReader_test.cpp | 63 ------- 9 files changed, 94 insertions(+), 207 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index f82c8ef1fd..fc8c7c39f9 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -358,6 +358,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.displayId = args.displayId; event.downTime = args.downTime; event.eventTime = args.eventTime; + event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); event.actionIndex = getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); @@ -375,7 +376,6 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.pointerProperties = pointerProperties; event.pointerCoords = pointerCoords; - event.deviceTimestamp = args.deviceTimestamp; event.frames = convertVideoFrames(args.videoFrames); return event; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 0498e87732..de639772a8 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -92,10 +92,10 @@ NotifyMotionArgs::NotifyMotionArgs( uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, const std::vector& videoFrames) + int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + const std::vector& videoFrames) : NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source), @@ -108,7 +108,6 @@ NotifyMotionArgs::NotifyMotionArgs( buttonState(buttonState), classification(classification), edgeFlags(edgeFlags), - deviceTimestamp(deviceTimestamp), pointerCount(pointerCount), xPrecision(xPrecision), yPrecision(yPrecision), @@ -135,7 +134,6 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) buttonState(other.buttonState), classification(other.classification), edgeFlags(other.edgeFlags), - deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount), xPrecision(other.xPrecision), yPrecision(other.yPrecision), @@ -159,7 +157,7 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { policyFlags == rhs.policyFlags && action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && buttonState == rhs.buttonState && classification == rhs.classification && - edgeFlags == rhs.edgeFlags && deviceTimestamp == rhs.deviceTimestamp && + edgeFlags == rhs.edgeFlags && pointerCount == rhs.pointerCount // PointerProperties and PointerCoords are compared separately below && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 9e5990964c..b4c6b3326c 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -1731,10 +1731,12 @@ void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { // --- MultiTouchMotionAccumulator --- -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : - mCurrentSlot(-1), mSlots(nullptr), mSlotCount(0), mUsingSlotsProtocol(false), - mHaveStylus(false), mDeviceTimestamp(0) { -} +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), + mSlots(nullptr), + mSlotCount(0), + mUsingSlotsProtocol(false), + mHaveStylus(false) {} MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { delete[] mSlots; @@ -1774,7 +1776,6 @@ void MultiTouchMotionAccumulator::reset(InputDevice* device) { } else { clearSlots(-1); } - mDeviceTimestamp = 0; } void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { @@ -1868,8 +1869,6 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; - } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { - mDeviceTimestamp = rawEvent->value; } } @@ -2849,8 +2848,7 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); @@ -2861,8 +2859,7 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); @@ -2876,8 +2873,7 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); @@ -2893,11 +2889,9 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, - /* videoFrames */ {}); + yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&hoverArgs); } @@ -2910,11 +2904,9 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, - /* videoFrames */ {}); + yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } } @@ -3057,9 +3049,8 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } @@ -4781,7 +4772,6 @@ void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { int32_t buttonState = mCurrentCookedState.buttonState; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4804,7 +4794,6 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4839,7 +4828,6 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, @@ -4854,7 +4842,6 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4873,7 +4860,6 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4889,7 +4875,6 @@ void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { int32_t metaState = getContext()->getGlobalMetaState(); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastCookedState.buttonState, 0, - mLastCookedState.deviceTimestamp, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, @@ -4906,7 +4891,6 @@ void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFl if (!mSentHoverEnter) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4918,7 +4902,6 @@ void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFl dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -4938,7 +4921,6 @@ void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, @@ -4956,7 +4938,6 @@ void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { buttonState |= actionButton; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, @@ -4975,8 +4956,6 @@ void TouchInputMapper::cookPointerData() { uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.deviceTimestamp = - mCurrentRawState.deviceTimestamp; mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; mCurrentCookedState.cookedPointerData.hoveringIdBits = mCurrentRawState.rawPointerData.hoveringIdBits; @@ -5368,13 +5347,11 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); if (!dispatchedGestureIdBits.isEmpty()) { if (cancelPreviousGesture) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - dispatchedGestureIdBits, -1, 0, - 0, mPointerGesture.downTime); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); dispatchedGestureIdBits.clear(); } else { @@ -5391,7 +5368,6 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, @@ -5404,13 +5380,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Send motion events for all pointers that moved. if (moveNeeded) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - dispatchedGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); } // Send motion events for all pointers that went down. @@ -5427,7 +5402,6 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - /* deviceTimestamp */ 0, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, @@ -5437,13 +5411,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Send motion events for hover. if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { // Synthesize a hover move event after all pointers go up to indicate that @@ -5467,9 +5440,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, - x, y, mPointerGesture.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -5496,13 +5468,11 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) if (!mPointerGesture.lastGestureIdBits.isEmpty()) { int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); } // Reset the current pointer gesture. @@ -6395,10 +6365,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6409,10 +6379,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6425,11 +6395,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6437,10 +6406,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6452,11 +6421,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6464,10 +6432,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6486,10 +6454,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6510,11 +6478,12 @@ void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { } void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, int32_t edgeFlags, uint32_t deviceTimestamp, - const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, - float xPrecision, float yPrecision, nsecs_t downTime) { + int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, + const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, + BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime) { PointerCoords pointerCoords[MAX_POINTERS]; PointerProperties pointerProperties[MAX_POINTERS]; uint32_t pointerCount = 0; @@ -6558,9 +6527,9 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount, - pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, - yCursorPosition, downTime, std::move(frames)); + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); getListener()->notifyMotion(&args); } @@ -7043,7 +7012,6 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { outCount += 1; } - outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp(); outState->rawPointerData.pointerCount = outCount; mPointerIdBits = newPointerIdBits; @@ -7484,8 +7452,8 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&args); diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 9777779e7d..0c08e7da38 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -717,7 +717,6 @@ public: inline size_t getSlotCount() const { return mSlotCount; } inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; } private: int32_t mCurrentSlot; @@ -725,7 +724,6 @@ private: size_t mSlotCount; bool mUsingSlotsProtocol; bool mHaveStylus; - uint32_t mDeviceTimestamp; void clearSlots(int32_t initialSlot); }; @@ -1174,7 +1172,6 @@ protected: struct RawState { nsecs_t when; - uint32_t deviceTimestamp; // Raw pointer sample data. RawPointerData rawPointerData; @@ -1187,7 +1184,6 @@ protected: void copyFrom(const RawState& other) { when = other.when; - deviceTimestamp = other.deviceTimestamp; rawPointerData.copyFrom(other.rawPointerData); buttonState = other.buttonState; rawVScroll = other.rawVScroll; @@ -1196,7 +1192,6 @@ protected: void clear() { when = 0; - deviceTimestamp = 0; rawPointerData.clear(); buttonState = 0; rawVScroll = 0; @@ -1205,7 +1200,6 @@ protected: }; struct CookedState { - uint32_t deviceTimestamp; // Cooked pointer sample data. CookedPointerData cookedPointerData; @@ -1217,7 +1211,6 @@ protected: int32_t buttonState; void copyFrom(const CookedState& other) { - deviceTimestamp = other.deviceTimestamp; cookedPointerData.copyFrom(other.cookedPointerData); fingerIdBits = other.fingerIdBits; stylusIdBits = other.stylusIdBits; @@ -1226,7 +1219,6 @@ protected: } void clear() { - deviceTimestamp = 0; cookedPointerData.clear(); fingerIdBits.clear(); stylusIdBits.clear(); @@ -1634,7 +1626,6 @@ private: void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, - uint32_t deviceTimestamp, const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index 57c894bf8b..0dcd2f9c38 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -107,13 +107,7 @@ struct NotifyMotionArgs : public NotifyArgs { */ MotionClassification classification; int32_t edgeFlags; - /** - * A timestamp in the input device's time base, not the platform's. - * The units are microseconds since the last reset. - * This can only be compared to other device timestamps from the same device. - * This value will overflow after a little over an hour. - */ - uint32_t deviceTimestamp; + uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; @@ -134,8 +128,7 @@ struct NotifyMotionArgs : public NotifyArgs { NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, - uint32_t deviceTimestamp, uint32_t pointerCount, + MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp index ba1c7c9284..f58b6281df 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -42,9 +42,9 @@ static NotifyMotionArgs generateBasicMotionArgs() { AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, - 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, - 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties, + &coords, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, {} /*videoFrames*/); return motionArgs; diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 9bc4282d9c..40086ef708 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -42,9 +42,9 @@ static NotifyMotionArgs generateBasicMotionArgs() { AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5 /*deviceTimestamp*/, - 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/, - 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties, + &coords, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, {} /*videoFrames*/); return motionArgs; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c28a6214d6..a86dcbc552 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -608,8 +608,8 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords, + /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d35302885d..e10883485a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -4688,7 +4688,6 @@ protected: void processSlot(MultiTouchInputMapper* mapper, int32_t slot); void processToolType(MultiTouchInputMapper* mapper, int32_t toolType); void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value); - void processTimestamp(MultiTouchInputMapper* mapper, uint32_t value); void processMTSync(MultiTouchInputMapper* mapper); void processSync(MultiTouchInputMapper* mapper); }; @@ -4804,10 +4803,6 @@ void MultiTouchInputMapperTest::processKey( process(mapper, ARBITRARY_TIME, EV_KEY, code, value); } -void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) { - process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value); -} - void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) { process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0); } @@ -6190,64 +6185,6 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfIts toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } -TEST_F(MultiTouchInputMapperTest, Process_HandlesTimestamp) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); - - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - NotifyMotionArgs args; - - // By default, deviceTimestamp should be zero - processPosition(mapper, 100, 100); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(0U, args.deviceTimestamp); - - // Now the timestamp of 1000 is reported by evdev and should appear in MotionArgs - processPosition(mapper, 0, 0); - processTimestamp(mapper, 1000); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1000U, args.deviceTimestamp); -} - -TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); - - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - NotifyMotionArgs args; - - // Send a touch event with a timestamp - processPosition(mapper, 100, 100); - processTimestamp(mapper, 1); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1U, args.deviceTimestamp); - - // Since the data accumulates, and new timestamp has not arrived, deviceTimestamp won't change - processPosition(mapper, 100, 200); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1U, args.deviceTimestamp); - - mapper->reset(/* when */ 0); - // After the mapper is reset, deviceTimestamp should become zero again - processPosition(mapper, 100, 300); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(0U, args.deviceTimestamp); -} - /** * Set the input device port <--> display port associations, and check that the * events are routed to the display that matches the display port. -- cgit v1.2.3-59-g8ed1b From ab0ab9c57c8fa9d8c9648734fea74ee010e28e8c Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 10 Jul 2019 18:58:28 -0700 Subject: Address comments from a previous change. The original change is 00f511d329924824b1961e9472c3a06683fc2216. Bug: 134788085 Test: atest libinput_tests Change-Id: I1f3326067f94fe6a09850f4389483e60fa57a8d4 --- include/input/Input.h | 3 +++ libs/input/Input.cpp | 3 +-- libs/input/tests/InputEvent_test.cpp | 10 +++++++--- services/inputflinger/InputDispatcher.cpp | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index a97624658c..ad8c233577 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -476,6 +477,8 @@ public: float getYCursorPosition() const; + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 3266b0740d..dc4978b836 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 -#include #include #include @@ -434,7 +433,7 @@ void MotionEvent::transform(const float matrix[9]) { transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to cursor position. - if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) { float x = mXCursorPosition + oldXOffset; float y = mYCursorPosition + oldYOffset; transformPoint(matrix, x, y, &x, &y); diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index ec34f3e652..b879de6a74 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -603,9 +603,13 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } - // Check cursor positions. - ASSERT_NEAR(sinf(PI_180 * (90 + ROTATION)) * RADIUS, event.getXCursorPosition(), 0.001); - ASSERT_NEAR(-cosf(PI_180 * (90 + ROTATION)) * RADIUS, event.getYCursorPosition(), 0.001); + // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center + // of the circle is (3, 2), so the cursor position is to the right of the center of the circle. + // The choice of triangular functions in this test defines the angle of rotation clockwise + // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the + // triangular function so that we don't have to add the 90 degrees. + ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001); // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index be1370747c..1d8c365702 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -2766,7 +2766,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ", policyFlags=0x%x, " "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "mYCursorPosition=%f, downTime=%" PRId64, + "yCursorPosition=%f, downTime=%" PRId64, args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, -- cgit v1.2.3-59-g8ed1b From c5ae0dc56d828a85b8b609ca29923e58dea3b807 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 10 Jul 2019 15:51:18 -0700 Subject: Improve input devices changed logs Currently, "reconfiguring input devices" log is hard to read, because it dumps the values as hex. That means, the reader has to go into code to understand what the actual changes are. Furthermore, if the defines are added/removed/modified between releases, it's even harder to debug. To improve this, convert to the actual values before printing. This will also help confirm that these are only ever invoked with a single change. Maybe we can change to enum class then. Bug: 137212522 Test: adb logcat | grep -i "input devices" Change-Id: I6b40d1c785df8a57c9e2619210906d326844d69d Before: InputReader: Reconfiguring input devices. changes=0x00000010 After: InputReader: Reconfiguring input devices, changes=KEYBOARD_LAYOUTS | --- services/inputflinger/InputReader.cpp | 3 +- services/inputflinger/InputReaderBase.cpp | 38 +++++++++++++++++++++++++ services/inputflinger/include/InputReaderBase.h | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index b4c6b3326c..eee49d5e2a 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -559,7 +559,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); if (changes) { - ALOGI("Reconfiguring input devices. changes=0x%08x", changes); + ALOGI("Reconfiguring input devices, changes=%s", + InputReaderConfiguration::changesToString(changes).c_str()); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index f48a64551e..bc53cf52cc 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -49,6 +49,44 @@ bool InputReaderThread::threadLoop() { // --- InputReaderConfiguration --- +std::string InputReaderConfiguration::changesToString(uint32_t changes) { + if (changes == 0) { + return ""; + } + std::string result; + if (changes & CHANGE_POINTER_SPEED) { + result += "POINTER_SPEED | "; + } + if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) { + result += "POINTER_GESTURE_ENABLEMENT | "; + } + if (changes & CHANGE_DISPLAY_INFO) { + result += "DISPLAY_INFO | "; + } + if (changes & CHANGE_SHOW_TOUCHES) { + result += "SHOW_TOUCHES | "; + } + if (changes & CHANGE_KEYBOARD_LAYOUTS) { + result += "KEYBOARD_LAYOUTS | "; + } + if (changes & CHANGE_DEVICE_ALIAS) { + result += "DEVICE_ALIAS | "; + } + if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) { + result += "TOUCH_AFFINE_TRANSFORMATION | "; + } + if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) { + result += "EXTERNAL_STYLUS_PRESENCE | "; + } + if (changes & CHANGE_ENABLED_STATE) { + result += "ENABLED_STATE | "; + } + if (changes & CHANGE_MUST_REOPEN) { + result += "MUST_REOPEN | "; + } + return result; +} + std::optional InputReaderConfiguration::getDisplayViewportByUniqueId( const std::string& uniqueDisplayId) const { if (uniqueDisplayId.empty()) { diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 8ad5dd0785..c7720cbf59 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -270,6 +270,8 @@ struct InputReaderConfiguration { pointerGestureZoomSpeedRatio(0.3f), showTouches(false), pointerCapture(false) { } + static std::string changesToString(uint32_t changes); + std::optional getDisplayViewportByType(ViewportType type) const; std::optional getDisplayViewportByUniqueId(const std::string& uniqueDisplayId) const; -- cgit v1.2.3-59-g8ed1b From a03f0debad92921ba52d33e7e06f0064703e85ad Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 10 Jul 2019 15:54:34 -0700 Subject: Use std::set instead of SortedVector We only care whether a certain input device is present in a list or not. For that, we can just use an std::set and not have to use custom SortedVector data structure. This makes it easier for someone already familiar with c++ but not necessarily Android to follow the code. It also ensures that the data structure is properly maintained going forward. Bug: 137212522 Test: atest libinput_tests inputflinger_tests Change-Id: I7808b41524ae0b87dbf1aaf7a49afa64b14b3eb9 --- services/inputflinger/InputReader.cpp | 4 ++-- services/inputflinger/include/InputReaderBase.h | 6 +++--- services/inputflinger/tests/InputReader_test.cpp | 16 ++-------------- 3 files changed, 7 insertions(+), 19 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index eee49d5e2a..2de5ffa0eb 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -1094,8 +1094,8 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - ssize_t index = config->disabledDevices.indexOf(mId); - bool enabled = index < 0; + auto it = config->disabledDevices.find(mId); + bool enabled = it == config->disabledDevices.end(); setEnabled(enabled, when); } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index c7720cbf59..271051499c 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -27,11 +27,11 @@ #include #include #include -#include -#include #include #include +#include +#include #include #include @@ -250,7 +250,7 @@ struct InputReaderConfiguration { bool pointerCapture; // The set of currently disabled input devices. - SortedVector disabledDevices; + std::set disabledDevices; InputReaderConfiguration() : virtualKeyQuietTime(0), diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e10883485a..541de99623 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -202,21 +202,9 @@ public: mConfig.portAssociations.insert({inputPort, displayPort}); } - void addDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (currentlyEnabled) { - mConfig.disabledDevices.add(deviceId); - } - } + void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } - void removeDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (!currentlyEnabled) { - mConfig.disabledDevices.remove(deviceId); - } - } + void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } void setPointerController(int32_t deviceId, const sp& controller) { mPointerControllers.add(deviceId, controller); -- cgit v1.2.3-59-g8ed1b From 6fd1c77faa58db2bc607da2fc5a19e5d3aa59734 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 10 Jul 2019 16:52:31 -0700 Subject: Remove unused include The include is no longer needed. Bug: none Test: none Change-Id: I4eb97f3423a93d51c8fb4f7067dccdf28cce8434 --- services/inputflinger/include/InputReaderBase.h | 1 - 1 file changed, 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 271051499c..5d576b94f3 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-59-g8ed1b From 2d8cc8945eb1c1be5ce6a262613a5aae6e257d06 Mon Sep 17 00:00:00 2001 From: Pin-Yu Huang Date: Fri, 19 Jul 2019 04:00:44 +0000 Subject: Revert "Use std::set instead of SortedVector" This reverts commit a03f0debad92921ba52d33e7e06f0064703e85ad. Reason for revert: cause BB Change-Id: I5531b895947236ff5754334f3cd4660d07513293 --- services/inputflinger/InputReader.cpp | 4 ++-- services/inputflinger/include/InputReaderBase.h | 6 +++--- services/inputflinger/tests/InputReader_test.cpp | 16 ++++++++++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 2de5ffa0eb..eee49d5e2a 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -1094,8 +1094,8 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - auto it = config->disabledDevices.find(mId); - bool enabled = it == config->disabledDevices.end(); + ssize_t index = config->disabledDevices.indexOf(mId); + bool enabled = index < 0; setEnabled(enabled, when); } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 271051499c..c7720cbf59 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -27,11 +27,11 @@ #include #include #include +#include +#include #include #include -#include -#include #include #include @@ -250,7 +250,7 @@ struct InputReaderConfiguration { bool pointerCapture; // The set of currently disabled input devices. - std::set disabledDevices; + SortedVector disabledDevices; InputReaderConfiguration() : virtualKeyQuietTime(0), diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 541de99623..e10883485a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -202,9 +202,21 @@ public: mConfig.portAssociations.insert({inputPort, displayPort}); } - void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } + void addDisabledDevice(int32_t deviceId) { + ssize_t index = mConfig.disabledDevices.indexOf(deviceId); + bool currentlyEnabled = index < 0; + if (currentlyEnabled) { + mConfig.disabledDevices.add(deviceId); + } + } - void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } + void removeDisabledDevice(int32_t deviceId) { + ssize_t index = mConfig.disabledDevices.indexOf(deviceId); + bool currentlyEnabled = index < 0; + if (!currentlyEnabled) { + mConfig.disabledDevices.remove(deviceId); + } + } void setPointerController(int32_t deviceId, const sp& controller) { mPointerControllers.add(deviceId, controller); -- cgit v1.2.3-59-g8ed1b From c6f61190e7bb4579047f70f3c66282457c1e68d7 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 23 Jul 2019 18:12:31 +0000 Subject: Revert "Revert "Use std::set instead of SortedVector"" This reverts commit 2d8cc8945eb1c1be5ce6a262613a5aae6e257d06. Reason for revert: Likely build was having issues, and the original CLs were fine. From looking into the issues more closely, it appears that the topic was not merged atomically. Change-Id: I42393e615066b4cf10aa953757a3f43be5676a2f --- services/inputflinger/InputReader.cpp | 4 ++-- services/inputflinger/include/InputReaderBase.h | 6 +++--- services/inputflinger/tests/InputReader_test.cpp | 16 ++-------------- 3 files changed, 7 insertions(+), 19 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index eee49d5e2a..2de5ffa0eb 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -1094,8 +1094,8 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - ssize_t index = config->disabledDevices.indexOf(mId); - bool enabled = index < 0; + auto it = config->disabledDevices.find(mId); + bool enabled = it == config->disabledDevices.end(); setEnabled(enabled, when); } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index c7720cbf59..271051499c 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -27,11 +27,11 @@ #include #include #include -#include -#include #include #include +#include +#include #include #include @@ -250,7 +250,7 @@ struct InputReaderConfiguration { bool pointerCapture; // The set of currently disabled input devices. - SortedVector disabledDevices; + std::set disabledDevices; InputReaderConfiguration() : virtualKeyQuietTime(0), diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e10883485a..541de99623 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -202,21 +202,9 @@ public: mConfig.portAssociations.insert({inputPort, displayPort}); } - void addDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (currentlyEnabled) { - mConfig.disabledDevices.add(deviceId); - } - } + void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } - void removeDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (!currentlyEnabled) { - mConfig.disabledDevices.remove(deviceId); - } - } + void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } void setPointerController(int32_t deviceId, const sp& controller) { mPointerControllers.add(deviceId, controller); -- cgit v1.2.3-59-g8ed1b From 3bc7e09b2c69ebc70533fe1ce85cef1945133e85 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 24 Jul 2019 17:43:30 -0700 Subject: Do not use RefBase in EventHub Switch to std::unique_ptr instead. Unfortunately, have to also switch to shared_ptr because FakeEventHub implements EventHub but is also used to inject events when testing InputReader. Bug: 117684064 Test: atest inputflinger_tests Change-Id: I789ba0ca255b6a1f179711c61fc9da98a3c6a800 --- services/inputflinger/EventHub.h | 6 ++-- services/inputflinger/InputReader.cpp | 17 +++++---- services/inputflinger/InputReader.h | 11 +++--- services/inputflinger/InputReaderFactory.cpp | 2 +- services/inputflinger/tests/InputReader_test.cpp | 44 +++++++++++------------- 5 files changed, 41 insertions(+), 39 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h index eb4e8f2e50..6c3a4a2aa3 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/EventHub.h @@ -146,12 +146,11 @@ extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); * which keys are currently down. Finally, the event hub keeps track of the capabilities of * individual input devices, such as their class and the set of key codes that they support. */ -class EventHubInterface : public virtual RefBase { -protected: +class EventHubInterface { +public: EventHubInterface() { } virtual ~EventHubInterface() { } -public: // Synthetic raw event type codes produced when devices are added or removed. enum { // Sent when a device is added. @@ -319,7 +318,6 @@ public: virtual void dump(std::string& dump); virtual void monitor(); -protected: virtual ~EventHub(); private: diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 2de5ffa0eb..3e236a93ae 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -260,12 +260,17 @@ static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, // --- InputReader --- -InputReader::InputReader(const sp& eventHub, - const sp& policy, - const sp& listener) : - mContext(this), mEventHub(eventHub), mPolicy(policy), - mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1), - mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), +InputReader::InputReader(std::shared_ptr eventHub, + const sp& policy, + const sp& listener) + : mContext(this), + mEventHub(eventHub), + mPolicy(policy), + mNextSequenceNum(1), + mGlobalMetaState(0), + mGeneration(1), + mDisableVirtualKeysTimeout(LLONG_MIN), + mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 0c08e7da38..11ef9347dc 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -114,9 +114,9 @@ public: */ class InputReader : public InputReaderInterface { public: - InputReader(const sp& eventHub, - const sp& policy, - const sp& listener); + InputReader(std::shared_ptr eventHub, + const sp& policy, + const sp& listener); virtual ~InputReader(); virtual void dump(std::string& dump); @@ -181,7 +181,10 @@ private: Condition mReaderIsAliveCondition; - sp mEventHub; + // This could be unique_ptr, but due to the way InputReader tests are written, + // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test + // in parallel to passing it to the InputReader. + std::shared_ptr mEventHub; sp mPolicy; sp mQueuedListener; diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/InputReaderFactory.cpp index 3534f6b760..072499b9cd 100644 --- a/services/inputflinger/InputReaderFactory.cpp +++ b/services/inputflinger/InputReaderFactory.cpp @@ -22,7 +22,7 @@ namespace android { sp createInputReader( const sp& policy, const sp& listener) { - return new InputReader(new EventHub(), policy, listener); + return new InputReader(std::make_unique(), policy, listener); } } // namespace android \ No newline at end of file diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 541de99623..d95ac96124 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -325,14 +325,13 @@ class FakeEventHub : public EventHubInterface { List mEvents; std::unordered_map> mVideoFrames; -protected: +public: virtual ~FakeEventHub() { for (size_t i = 0; i < mDevices.size(); i++) { delete mDevices.valueAt(i); } } -public: FakeEventHub() { } void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) { @@ -760,7 +759,7 @@ private: // --- FakeInputReaderContext --- class FakeInputReaderContext : public InputReaderContext { - sp mEventHub; + std::shared_ptr mEventHub; sp mPolicy; sp mListener; int32_t mGlobalMetaState; @@ -769,12 +768,14 @@ class FakeInputReaderContext : public InputReaderContext { uint32_t mNextSequenceNum; public: - FakeInputReaderContext(const sp& eventHub, - const sp& policy, - const sp& listener) : - mEventHub(eventHub), mPolicy(policy), mListener(listener), - mGlobalMetaState(0), mNextSequenceNum(1) { - } + FakeInputReaderContext(std::shared_ptr eventHub, + const sp& policy, + const sp& listener) + : mEventHub(eventHub), + mPolicy(policy), + mListener(listener), + mGlobalMetaState(0), + mNextSequenceNum(1) {} virtual ~FakeInputReaderContext() { } @@ -999,12 +1000,10 @@ class InstrumentedInputReader : public InputReader { InputDevice* mNextDevice; public: - InstrumentedInputReader(const sp& eventHub, - const sp& policy, - const sp& listener) : - InputReader(eventHub, policy, listener), - mNextDevice(nullptr) { - } + InstrumentedInputReader(std::shared_ptr eventHub, + const sp& policy, + const sp& listener) + : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {} virtual ~InstrumentedInputReader() { if (mNextDevice) { @@ -1232,11 +1231,11 @@ class InputReaderTest : public testing::Test { protected: sp mFakeListener; sp mFakePolicy; - sp mFakeEventHub; + std::shared_ptr mFakeEventHub; sp mReader; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1248,7 +1247,6 @@ protected: mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } void addDevice(int32_t deviceId, const std::string& name, uint32_t classes, @@ -1575,7 +1573,7 @@ protected: static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; - sp mFakeEventHub; + std::shared_ptr mFakeEventHub; sp mFakePolicy; sp mFakeListener; FakeInputReaderContext* mFakeContext; @@ -1583,7 +1581,7 @@ protected: InputDevice* mDevice; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); @@ -1601,7 +1599,6 @@ protected: delete mFakeContext; mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } }; @@ -1770,14 +1767,14 @@ protected: static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; - sp mFakeEventHub; + std::shared_ptr mFakeEventHub; sp mFakePolicy; sp mFakeListener; FakeInputReaderContext* mFakeContext; InputDevice* mDevice; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); @@ -1795,7 +1792,6 @@ protected: delete mFakeContext; mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } void addConfigurationProperty(const char* key, const char* value) { -- cgit v1.2.3-59-g8ed1b From 49a350aedcc199b90a2992e1c59f753cf9843aa1 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 26 Jul 2019 18:44:23 -0700 Subject: Use std::function for commands While refactoring InputDispatcher to use unique_ptr in order to better understand the lifespands of different object, it became necessary to refactor commands into something more comprehensible. Bug: 70668286 Test: presubmit Change-Id: I273b4f75a32b2c4faf6f907de9f85e7323f2f90d --- services/inputflinger/InputDispatcher.cpp | 32 +++++++++++++++---------------- services/inputflinger/InputDispatcher.h | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index fb28d1b380..fc40eafd08 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -715,7 +715,7 @@ bool InputDispatcher::runCommandsLockedInterruptible() { CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); Command command = commandEntry->command; - (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' + command(*this, commandEntry); // commands are implicitly 'LockedInterruptible' commandEntry->connection.clear(); delete commandEntry; @@ -809,8 +809,8 @@ bool InputDispatcher::dispatchConfigurationChangedLocked( resetKeyRepeatLocked(); // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); commandEntry->eventTime = entry->eventTime; return true; } @@ -884,7 +884,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); + &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); if (focusedWindowHandle != nullptr) { @@ -1982,8 +1982,8 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } } - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doPokeUserActivityLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible); commandEntry->eventTime = eventEntry->eventTime; commandEntry->userActivityEventType = eventType; } @@ -2200,8 +2200,8 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a return; } - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); commandEntry->newToken = newToken; } @@ -4087,8 +4087,8 @@ ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputC void InputDispatcher::onDispatchCycleFinishedLocked( nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; @@ -4100,8 +4100,8 @@ void InputDispatcher::onDispatchCycleBrokenLocked( ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", connection->getInputChannelName().c_str()); - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); commandEntry->connection = connection; } @@ -4109,8 +4109,8 @@ void InputDispatcher::onFocusChangedLocked(const sp& oldFocus const sp& newFocus) { sp oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; sp newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyFocusChangedLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible); commandEntry->oldToken = oldToken; commandEntry->newToken = newToken; } @@ -4142,8 +4142,8 @@ void InputDispatcher::onANRLocked( mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); dumpDispatchStateLocked(mLastANRState); - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyANRLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index c30a8d6703..147437c1a2 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -644,7 +644,7 @@ private: // // Commands are implicitly 'LockedInterruptible'. struct CommandEntry; - typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); + typedef std::function Command; class Connection; struct CommandEntry : Link { -- cgit v1.2.3-59-g8ed1b From 4e68fbfecc6ab41bfa3d6c06d4e04ff4b098eb00 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 31 Jul 2019 14:00:52 -0700 Subject: Return early in doDispatchCycleFinishedLockedInterruptible Reduce the identation of the entire method by returning early if entry is null. Bug: 70668286 Test: none Change-Id: Ic9239c4f7e3566cd584a57973d9e8ed08d8c0287 --- services/inputflinger/InputDispatcher.cpp | 82 ++++++++++++++++--------------- 1 file changed, 42 insertions(+), 40 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index fc40eafd08..019815c2c5 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -4237,53 +4237,55 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( CommandEntry* commandEntry) { sp connection = commandEntry->connection; - nsecs_t finishTime = commandEntry->eventTime; + const nsecs_t finishTime = commandEntry->eventTime; uint32_t seq = commandEntry->seq; - bool handled = commandEntry->handled; + const bool handled = commandEntry->handled; // Handle post-event policy actions. DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); - if (dispatchEntry) { - nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; - if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); - } - - bool restartEvent; - if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); - restartEvent = afterKeyEventLockedInterruptible(connection, - dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); - restartEvent = afterMotionEventLockedInterruptible(connection, - dispatchEntry, motionEntry, handled); + if (!dispatchEntry) { + return; + } + + nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { + std::string msg = + StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", + connection->getWindowName().c_str(), eventDuration * 0.000001f); + dispatchEntry->eventEntry->appendDescription(msg); + ALOGI("%s", msg.c_str()); + } + + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = + afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, + handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Note that because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + if (dispatchEntry == connection->findWaitQueueEntry(seq)) { + connection->waitQueue.dequeue(dispatchEntry); + traceWaitQueueLength(connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.enqueueAtHead(dispatchEntry); + traceOutboundQueueLength(connection); } else { - restartEvent = false; - } - - // Dequeue the event and start the next cycle. - // Note that because the lock might have been released, it is possible that the - // contents of the wait queue to have been drained, so we need to double-check - // a few things. - if (dispatchEntry == connection->findWaitQueueEntry(seq)) { - connection->waitQueue.dequeue(dispatchEntry); - traceWaitQueueLength(connection); - if (restartEvent && connection->status == Connection::STATUS_NORMAL) { - connection->outboundQueue.enqueueAtHead(dispatchEntry); - traceOutboundQueueLength(connection); - } else { - releaseDispatchEntry(dispatchEntry); - } + releaseDispatchEntry(dispatchEntry); } - - // Start the next dispatch cycle for this connection. - startDispatchCycleLocked(now(), connection); } + + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); } bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& connection, -- cgit v1.2.3-59-g8ed1b From 8384682fd25b54921fd74288788f2a9299dd8dba Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Thu, 18 Jul 2019 15:17:40 -0700 Subject: Seperate LatencyStatistics from InputReader Test: atest LatencyStatisticsTest Change-Id: I22a39cd5bef7fa9180bc1ee1fd9478a2cf872e83 --- include/input/LatencyStatistics.h | 59 +++++++++++++++++++ libs/input/Android.bp | 1 + libs/input/LatencyStatistics.cpp | 90 +++++++++++++++++++++++++++++ libs/input/tests/Android.bp | 1 + libs/input/tests/LatencyStatistics_test.cpp | 72 +++++++++++++++++++++++ services/inputflinger/InputReader.cpp | 21 +++---- services/inputflinger/InputReader.h | 72 ++--------------------- 7 files changed, 237 insertions(+), 79 deletions(-) create mode 100644 include/input/LatencyStatistics.h create mode 100644 libs/input/LatencyStatistics.cpp create mode 100644 libs/input/tests/LatencyStatistics_test.cpp (limited to 'services/inputflinger') diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h new file mode 100644 index 0000000000..bd86266901 --- /dev/null +++ b/include/input/LatencyStatistics.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_STATISTICS_H +#define _UI_INPUT_STATISTICS_H + +#include + +#include + +namespace android { + +class LatencyStatistics { +private: + /* Minimum sample recorded */ + float mMin; + /* Maximum sample recorded */ + float mMax; + /* Sum of all samples recorded */ + float mSum; + /* Sum of all the squares of samples recorded */ + float mSum2; + /* Count of all samples recorded */ + size_t mCount; + /* The last time statistics were reported */ + std::chrono::steady_clock::time_point mLastReportTime; + /* Statistics Report Frequency */ + const std::chrono::seconds mReportPeriod; + +public: + LatencyStatistics(std::chrono::seconds period); + + void addValue(float); + void reset(); + bool shouldReport(); + + float getMean(); + float getMin(); + float getMax(); + float getStDev(); + size_t getCount(); +}; + +} // namespace android + +#endif // _UI_INPUT_STATISTICS_H diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2d788119cd..17138506e5 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -48,6 +48,7 @@ cc_library { "InputTransport.cpp", "InputWindow.cpp", "ISetInputWindowsListener.cpp", + "LatencyStatistics.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp new file mode 100644 index 0000000000..e343578e00 --- /dev/null +++ b/libs/input/LatencyStatistics.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include + +namespace android { + +LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) { + reset(); +} + +/** + * Add a raw value to the statistics + */ +void LatencyStatistics::addValue(float value) { + if (value < mMin) { + mMin = value; + } + if (value > mMax) { + mMax = value; + } + mSum += value; + mSum2 += value * value; + mCount++; +} + +/** + * Get the mean. Should not be called if no samples have been added. + */ +float LatencyStatistics::getMean() { + return mSum / mCount; +} + +/** + * Get the standard deviation. Should not be called if no samples have been added. + */ +float LatencyStatistics::getStDev() { + float mean = getMean(); + return sqrt(mSum2 / mCount - mean * mean); +} + +float LatencyStatistics::getMin() { + return mMin; +} + +float LatencyStatistics::getMax() { + return mMax; +} + +size_t LatencyStatistics::getCount() { + return mCount; +} + +/** + * Reset internal state. The variable 'when' is the time when the data collection started. + * Call this to start a new data collection window. + */ +void LatencyStatistics::reset() { + mMax = std::numeric_limits::lowest(); + mMin = std::numeric_limits::max(); + mSum = 0; + mSum2 = 0; + mCount = 0; + mLastReportTime = std::chrono::steady_clock::now(); +} + +bool LatencyStatistics::shouldReport() { + std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; + return mCount != 0 && timeSinceReport > mReportPeriod; +} + +} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index ade931e01a..c1c35e1b89 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -7,6 +7,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "InputWindow_test.cpp", + "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", ], diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp new file mode 100644 index 0000000000..6d1cab4187 --- /dev/null +++ b/libs/input/tests/LatencyStatistics_test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace android { +namespace test { + +TEST(LatencyStatisticsTest, ResetStats) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + stats.addValue(19.3); + stats.addValue(20); + stats.reset(); + + ASSERT_EQ(stats.getCount(), 0u); + ASSERT_EQ(std::isnan(stats.getStDev()), true); + ASSERT_EQ(std::isnan(stats.getMean()), true); +} + +TEST(LatencyStatisticsTest, AddStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + + ASSERT_EQ(stats.getMin(), 5.0); + ASSERT_EQ(stats.getMax(), 5.0); + ASSERT_EQ(stats.getCount(), 1u); + ASSERT_EQ(stats.getMean(), 5.0); + ASSERT_EQ(stats.getStDev(), 0.0); +} + +TEST(LatencyStatisticsTest, AddMultipleStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(4.0); + stats.addValue(6.0); + stats.addValue(8.0); + stats.addValue(10.0); + + float stdev = stats.getStDev(); + + ASSERT_EQ(stats.getMin(), 4.0); + ASSERT_EQ(stats.getMax(), 10.0); + ASSERT_EQ(stats.getCount(), 4u); + ASSERT_EQ(stats.getMean(), 7.0); + ASSERT_EQ(stdev * stdev, 5.0); +} + +TEST(LatencyStatisticsTest, ShouldReportStats) { + LatencyStatistics stats{0min}; + stats.addValue(5.0); + + ASSERT_EQ(stats.shouldReport(), true); +} + +} // namespace test +} // namespace android \ No newline at end of file diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index eee49d5e2a..df5dcafec9 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -85,9 +85,6 @@ static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); // data. static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); -// How often to report input event statistics -static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); - // --- Static Functions --- template @@ -4314,16 +4311,14 @@ void TouchInputMapper::clearStylusDataPendingFlags() { } void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - nsecs_t now = systemTime(CLOCK_MONOTONIC); - nsecs_t latency = now - evdevTime; - mStatistics.addValue(nanoseconds_to_microseconds(latency)); - nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; - if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, - mStatistics.min, mStatistics.max, - mStatistics.mean(), mStatistics.stdev(), mStatistics.count); - mStatistics.reset(now); - } + if (mStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.getMin(), + mStatistics.getMax(), mStatistics.getMean(), + mStatistics.getStDev(), mStatistics.getCount()); + mStatistics.reset(); + } + nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); + mStatistics.addValue(latency); } void TouchInputMapper::process(const RawEvent* rawEvent) { diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 0c08e7da38..33763b6cdf 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -24,14 +24,15 @@ #include #include +#include #include #include #include -#include +#include #include +#include #include #include -#include #include #include @@ -569,69 +570,6 @@ struct CookedPointerData { } }; -/** - * Basic statistics information. - * Keep track of min, max, average, and standard deviation of the received samples. - * Used to report latency information about input events. - */ -struct LatencyStatistics { - float min; - float max; - // Sum of all samples - float sum; - // Sum of squares of all samples - float sum2; - // The number of samples - size_t count; - // The last time statistics were reported. - nsecs_t lastReportTime; - - LatencyStatistics() { - reset(systemTime(SYSTEM_TIME_MONOTONIC)); - } - - inline void addValue(float x) { - if (x < min) { - min = x; - } - if (x > max) { - max = x; - } - sum += x; - sum2 += x * x; - count++; - } - - // Get the average value. Should not be called if no samples have been added. - inline float mean() { - if (count == 0) { - return 0; - } - return sum / count; - } - - // Get the standard deviation. Should not be called if no samples have been added. - inline float stdev() { - if (count == 0) { - return 0; - } - float average = mean(); - return sqrt(sum2 / count - average * average); - } - - /** - * Reset internal state. The variable 'when' is the time when the data collection started. - * Call this to start a new data collection window. - */ - inline void reset(nsecs_t when) { - max = 0; - min = std::numeric_limits::max(); - sum = 0; - sum2 = 0; - count = 0; - lastReportTime = when; - } -}; /* Keeps track of the state of single-touch protocol. */ class SingleTouchMotionAccumulator { @@ -1571,8 +1509,10 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; + static constexpr std::chrono::duration STATS_REPORT_PERIOD = 5min; + // Latency statistics for touch events - struct LatencyStatistics mStatistics; + LatencyStatistics mStatistics{STATS_REPORT_PERIOD}; std::optional findViewport(); -- cgit v1.2.3-59-g8ed1b From 3d3fa52c8bbe481ffb87d75e24725444bef0b939 Mon Sep 17 00:00:00 2001 From: Atif Niyaz Date: Thu, 25 Jul 2019 11:12:39 -0700 Subject: Move LatencyStatistics collection from InputReader to InputTransport This move allows us to grab a bigger context of how much time a motion event is spent from the kernel to right before the event was published. Test: Modified LatencyStats to report ever 10 seconds. Utilized logs to check that logs were reporting every 10 seconds. Checked also using logs that data was being added and calculated correctly. Change-Id: Iee0f9a2155e93ae77de5a5cd8b9fd1506186c60f Signed-off-by: Atif Niyaz --- include/input/InputTransport.h | 12 ++++++++++-- libs/input/Android.bp | 3 ++- libs/input/InputTransport.cpp | 16 ++++++++++++++++ services/inputflinger/Android.bp | 1 - services/inputflinger/InputReader.cpp | 13 ------------- services/inputflinger/InputReader.h | 8 -------- 6 files changed, 28 insertions(+), 25 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index df23f613c8..690e0a11c8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -31,13 +31,16 @@ #include +#include + #include #include +#include +#include #include -#include #include +#include #include -#include namespace android { class Parcel; @@ -286,7 +289,12 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: + static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; + sp mChannel; + LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; + + void reportTouchEventForStatistics(nsecs_t evdevTime); }; /* diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 17138506e5..7749e66c4d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -56,7 +56,8 @@ cc_library { shared_libs: [ "libutils", "libbinder", - "libui" + "libui", + "libstatslog", ], sanitize: { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 904a6feb03..2ff301e270 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -34,6 +34,7 @@ #include #include +#include using android::base::StringPrintf; @@ -531,6 +532,10 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } + + if (source == AINPUT_SOURCE_TOUCHSCREEN) { + reportTouchEventForStatistics(eventTime); + } return mChannel->sendMessage(&msg); } @@ -557,6 +562,17 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle return OK; } +void InputPublisher::reportTouchEventForStatistics(nsecs_t evdevTime) { + if (mTouchStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), + mTouchStatistics.getMax(), mTouchStatistics.getMean(), + mTouchStatistics.getStDev(), mTouchStatistics.getCount()); + mTouchStatistics.reset(); + } + nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); + mTouchStatistics.addValue(latency); +} + // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 8dd4d1df63..bdee6fe043 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -88,7 +88,6 @@ cc_library_shared { "libui", "libutils", "libhardware_legacy", - "libstatslog", ], header_libs: [ diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index df5dcafec9..d565e3ab38 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #define INDENT " " #define INDENT2 " " @@ -4310,24 +4309,12 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } -void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - if (mStatistics.shouldReport()) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.getMin(), - mStatistics.getMax(), mStatistics.getMean(), - mStatistics.getStDev(), mStatistics.getCount()); - mStatistics.reset(); - } - nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); - mStatistics.addValue(latency); -} - void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - reportEventForStatistics(rawEvent->when); sync(rawEvent->when); } } diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 33763b6cdf..e434869996 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -1509,11 +1508,6 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; - static constexpr std::chrono::duration STATS_REPORT_PERIOD = 5min; - - // Latency statistics for touch events - LatencyStatistics mStatistics{STATS_REPORT_PERIOD}; - std::optional findViewport(); void resetExternalStylus(); @@ -1582,8 +1576,6 @@ private: static void assignPointerIds(const RawState* last, RawState* current); - void reportEventForStatistics(nsecs_t evdevTime); - const char* modeToString(DeviceMode deviceMode); }; -- cgit v1.2.3-59-g8ed1b From 13bda6c5f4d9017da66054a526d30ddfc9c188c4 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 29 Jul 2019 08:34:33 -0700 Subject: Use std::deque for DispatchEntry'ies. Currently, there is a custom Queue class being used in InputDispatcher. But that class makes a lot of assumptions about the memory management in the queue. This also eliminates the possibility of using std::unique_ptr inside dispatcher. First refactor to remove the custom Queue from dispatcher. Bug: 70668286 Test: SANITIZE_TARGET=hwaddress atest libinput_tests inputflinger_tests Change-Id: Ie5919e2f2da11424e0cb48e9f960d73abaf59f46 --- services/inputflinger/InputDispatcher.cpp | 111 ++++++++++++++++-------------- services/inputflinger/InputDispatcher.h | 13 ++-- 2 files changed, 68 insertions(+), 56 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 019815c2c5..32cb04eaed 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -1873,8 +1873,9 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT // If the connection is backed up then keep waiting. if (connection->inputPublisherBlocked) { return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %d. Wait queue length: %d.", - targetType, connection->outboundQueue.count(), connection->waitQueue.count()); + "Outbound queue length: %zu. Wait queue length: %zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); } // Ensure that the dispatch queues aren't too far backed up for this event. @@ -1890,11 +1891,13 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT // often anticipate pending UI changes when typing on a keyboard. // To obtain this behavior, we must serialize key events with respect to all // prior input events. - if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) { + if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %d. Wait queue length: %d.", - targetType, connection->outboundQueue.count(), connection->waitQueue.count()); + "finished processing all of the input events that were previously " + "delivered to it. Outbound queue length: %zu. Wait queue length: " + "%zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); } } else { // Touch events can always be sent to a window immediately because the user intended @@ -1912,15 +1915,18 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT // The one case where we pause input event delivery is when the wait queue is piling // up with lots of events because the application is not responding. // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.isEmpty() - && currentTime >= connection->waitQueue.head->deliveryTime - + STREAM_AHEAD_EVENT_TIMEOUT) { + if (!connection->waitQueue.empty() && + currentTime >= + connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to it over " - "%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.count(), - (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f); + "finished processing certain input events that were delivered to " + "it over " + "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " + "%0.1fms.", + targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, + connection->waitQueue.size(), + (currentTime - connection->waitQueue.front()->deliveryTime) * + 0.000001f); } } return ""; @@ -2053,7 +2059,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, ATRACE_NAME(message.c_str()); } - bool wasEmpty = connection->outboundQueue.isEmpty(); + bool wasEmpty = connection->outboundQueue.empty(); // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, @@ -2070,7 +2076,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. - if (wasEmpty && !connection->outboundQueue.isEmpty()) { + if (wasEmpty && !connection->outboundQueue.empty()) { startDispatchCycleLocked(currentTime, connection); } } @@ -2173,7 +2179,7 @@ void InputDispatcher::enqueueDispatchEntryLocked( } // Enqueue the dispatch entry. - connection->outboundQueue.enqueueAtTail(dispatchEntry); + connection->outboundQueue.push_back(dispatchEntry); traceOutboundQueueLength(connection); } @@ -2217,9 +2223,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str()); #endif - while (connection->status == Connection::STATUS_NORMAL - && !connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head; + while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; // Publish the event. @@ -2301,7 +2306,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Check the result. if (status) { if (status == WOULD_BLOCK) { - if (connection->waitQueue.isEmpty()) { + if (connection->waitQueue.empty()) { ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " "This is unexpected because the wait queue is empty, so the pipe " "should be empty and we shouldn't have any problems writing an " @@ -2327,9 +2332,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Re-enqueue the event on the wait queue. - connection->outboundQueue.dequeue(dispatchEntry); + connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), + connection->outboundQueue.end(), + dispatchEntry)); traceOutboundQueueLength(connection); - connection->waitQueue.enqueueAtTail(dispatchEntry); + connection->waitQueue.push_back(dispatchEntry); traceWaitQueueLength(connection); } } @@ -2360,9 +2367,9 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, #endif // Clear the dispatch queues. - drainDispatchQueue(&connection->outboundQueue); + drainDispatchQueue(connection->outboundQueue); traceOutboundQueueLength(connection); - drainDispatchQueue(&connection->waitQueue); + drainDispatchQueue(connection->waitQueue); traceWaitQueueLength(connection); // The connection appears to be unrecoverably broken. @@ -2377,9 +2384,10 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, } } -void InputDispatcher::drainDispatchQueue(Queue* queue) { - while (!queue->isEmpty()) { - DispatchEntry* dispatchEntry = queue->dequeueAtHead(); +void InputDispatcher::drainDispatchQueue(std::deque& queue) { + while (!queue.empty()) { + DispatchEntry* dispatchEntry = queue.front(); + queue.pop_front(); releaseDispatchEntry(dispatchEntry); } } @@ -3815,11 +3823,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { connection->getStatusLabel(), toString(connection->monitor), toString(connection->inputPublisherBlocked)); - if (!connection->outboundQueue.isEmpty()) { - dump += StringPrintf(INDENT3 "OutboundQueue: length=%u\n", - connection->outboundQueue.count()); - for (DispatchEntry* entry = connection->outboundQueue.head; entry; - entry = entry->next) { + if (!connection->outboundQueue.empty()) { + dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", + connection->outboundQueue.size()); + for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", @@ -3830,11 +3837,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT3 "OutboundQueue: \n"; } - if (!connection->waitQueue.isEmpty()) { - dump += StringPrintf(INDENT3 "WaitQueue: length=%u\n", - connection->waitQueue.count()); - for (DispatchEntry* entry = connection->waitQueue.head; entry; - entry = entry->next) { + if (!connection->waitQueue.empty()) { + dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", + connection->waitQueue.size()); + for (DispatchEntry* entry : connection->waitQueue) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " @@ -4242,10 +4248,12 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( const bool handled = commandEntry->handled; // Handle post-event policy actions. - DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); - if (!dispatchEntry) { + std::deque::iterator dispatchEntryIt = + connection->findWaitQueueEntry(seq); + if (dispatchEntryIt == connection->waitQueue.end()) { return; } + DispatchEntry* dispatchEntry = *dispatchEntryIt; nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { @@ -4273,11 +4281,13 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( // Note that because the lock might have been released, it is possible that the // contents of the wait queue to have been drained, so we need to double-check // a few things. - if (dispatchEntry == connection->findWaitQueueEntry(seq)) { - connection->waitQueue.dequeue(dispatchEntry); + dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt != connection->waitQueue.end()) { + dispatchEntry = *dispatchEntryIt; + connection->waitQueue.erase(dispatchEntryIt); traceWaitQueueLength(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { - connection->outboundQueue.enqueueAtHead(dispatchEntry); + connection->outboundQueue.push_front(dispatchEntry); traceOutboundQueueLength(connection); } else { releaseDispatchEntry(dispatchEntry); @@ -4504,7 +4514,7 @@ void InputDispatcher::traceOutboundQueueLength(const sp& connection) if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->outboundQueue.count()); + ATRACE_INT(counterName, connection->outboundQueue.size()); } } @@ -4512,7 +4522,7 @@ void InputDispatcher::traceWaitQueueLength(const sp& connection) { if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->waitQueue.count()); + ATRACE_INT(counterName, connection->waitQueue.size()); } } @@ -5148,13 +5158,14 @@ const char* InputDispatcher::Connection::getStatusLabel() const { } } -InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { - if (entry->seq == seq) { - return entry; +std::deque::iterator +InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { + for (std::deque::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { + if ((*it)->seq == seq) { + return it; } } - return nullptr; + return waitQueue.end(); } // --- InputDispatcher::Monitor diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 147437c1a2..d73c4d6dd7 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -33,9 +33,10 @@ #include #include +#include #include #include -#include +#include #include #include "InputListener.h" @@ -592,7 +593,7 @@ private: }; // Tracks the progress of dispatching a particular event to a particular connection. - struct DispatchEntry : Link { + struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 EventEntry* eventEntry; // the event to dispatch @@ -886,11 +887,11 @@ private: bool inputPublisherBlocked; // Queue of events that need to be published to the connection. - Queue outboundQueue; + std::deque outboundQueue; // Queue of events that have been published to the connection but that have not // yet received a "finished" response from the application. - Queue waitQueue; + std::deque waitQueue; explicit Connection(const sp& inputChannel, bool monitor); @@ -899,7 +900,7 @@ private: const std::string getWindowName() const; const char* getStatusLabel() const; - DispatchEntry* findWaitQueueEntry(uint32_t seq); + std::deque::iterator findWaitQueueEntry(uint32_t seq); }; struct Monitor { @@ -1221,7 +1222,7 @@ private: uint32_t seq, bool handled) REQUIRES(mLock); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp& connection, bool notify) REQUIRES(mLock); - void drainDispatchQueue(Queue* queue); + void drainDispatchQueue(std::deque& queue); void releaseDispatchEntry(DispatchEntry* dispatchEntry); static int handleReceiveCallback(int fd, int events, void* data); // The action sent should only be of type AMOTION_EVENT_* -- cgit v1.2.3-59-g8ed1b From 44a2aed57a1d016b2e0301d355a178c0d996c127 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 29 Jul 2019 08:59:52 -0700 Subject: Use std::deque for EventEntry queues Remove custom queue implementation from InputDispatcher. This will help with refactoring some of the manual memory management that is going on there, which will in turn help understand object lifecycles better. This will in turn help add some of the focus handling stuff. Bug: 70668286 Test: SANITIZE_TARGET=hwaddress atest libinput_tests inputflinger_tests Change-Id: Ifd10b7b137e59569c148c79609ca43a59acb0781 --- services/inputflinger/InputDispatcher.cpp | 82 +++++++++++++++-------------- services/inputflinger/InputDispatcher.h | 87 ++----------------------------- 2 files changed, 47 insertions(+), 122 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 32cb04eaed..656793a9ca 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -48,10 +48,11 @@ #include #include #include -#include #include #include #include +#include +#include #include #include @@ -358,7 +359,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { - if (mInboundQueue.isEmpty()) { + if (mInboundQueue.empty()) { if (isAppSwitchDue) { // The inbound queue is empty so the app switch key we were waiting // for will never arrive. Stop waiting for it. @@ -383,7 +384,8 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } else { // Inbound queue has at least one entry. - mPendingEvent = mInboundQueue.dequeueAtHead(); + mPendingEvent = mInboundQueue.front(); + mInboundQueue.pop_front(); traceInboundQueueLengthLocked(); } @@ -483,8 +485,8 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { - bool needWake = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(entry); + bool needWake = mInboundQueue.empty(); + mInboundQueue.push_back(entry); traceInboundQueueLengthLocked(); switch (entry->type) { @@ -544,9 +546,10 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { void InputDispatcher::addRecentEventLocked(EventEntry* entry) { entry->refCount += 1; - mRecentQueue.enqueueAtTail(entry); - if (mRecentQueue.count() > RECENT_QUEUE_MAX_SIZE) { - mRecentQueue.dequeueAtHead()->release(); + mRecentQueue.push_back(entry); + if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { + mRecentQueue.front()->release(); + mRecentQueue.pop_front(); } } @@ -703,35 +706,36 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { } bool InputDispatcher::haveCommandsLocked() const { - return !mCommandQueue.isEmpty(); + return !mCommandQueue.empty(); } bool InputDispatcher::runCommandsLockedInterruptible() { - if (mCommandQueue.isEmpty()) { + if (mCommandQueue.empty()) { return false; } do { - CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); - + CommandEntry* commandEntry = mCommandQueue.front(); + mCommandQueue.pop_front(); Command command = commandEntry->command; command(*this, commandEntry); // commands are implicitly 'LockedInterruptible' commandEntry->connection.clear(); delete commandEntry; - } while (! mCommandQueue.isEmpty()); + } while (!mCommandQueue.empty()); return true; } InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { CommandEntry* commandEntry = new CommandEntry(command); - mCommandQueue.enqueueAtTail(commandEntry); + mCommandQueue.push_back(commandEntry); return commandEntry; } void InputDispatcher::drainInboundQueueLocked() { - while (! mInboundQueue.isEmpty()) { - EventEntry* entry = mInboundQueue.dequeueAtHead(); + while (!mInboundQueue.empty()) { + EventEntry* entry = mInboundQueue.front(); + mInboundQueue.pop_front(); releaseInboundEventLocked(entry); } traceInboundQueueLengthLocked(); @@ -2915,8 +2919,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, policyFlags |= POLICY_FLAG_TRUSTED; } - EventEntry* firstInjectedEntry; - EventEntry* lastInjectedEntry; + std::queue injectedEntries; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { KeyEvent keyEvent; @@ -2949,12 +2952,13 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } mLock.lock(); - firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, - keyEvent.getKeyCode(), keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); - lastInjectedEntry = firstInjectedEntry; + KeyEntry* injectedEntry = + new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), + keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), + policyFlags, action, flags, keyEvent.getKeyCode(), + keyEvent.getScanCode(), keyEvent.getMetaState(), + keyEvent.getRepeatCount(), keyEvent.getDownTime()); + injectedEntries.push(injectedEntry); break; } @@ -2982,7 +2986,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - firstInjectedEntry = + MotionEntry* injectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, @@ -2993,7 +2997,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); - lastInjectedEntry = firstInjectedEntry; + injectedEntries.push(injectedEntry); for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; @@ -3010,8 +3014,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, motionEvent->getDownTime(), uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); - lastInjectedEntry->next = nextInjectedEntry; - lastInjectedEntry = nextInjectedEntry; + injectedEntries.push(nextInjectedEntry); } break; } @@ -3027,13 +3030,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } injectionState->refCount += 1; - lastInjectedEntry->injectionState = injectionState; + injectedEntries.back()->injectionState = injectionState; bool needWake = false; - for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) { - EventEntry* nextEntry = entry->next; - needWake |= enqueueInboundEventLocked(entry); - entry = nextEntry; + while (!injectedEntries.empty()) { + needWake |= enqueueInboundEventLocked(injectedEntries.front()); + injectedEntries.pop(); } mLock.unlock(); @@ -3764,9 +3766,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { nsecs_t currentTime = now(); // Dump recently dispatched or dropped events from oldest to newest. - if (!mRecentQueue.isEmpty()) { - dump += StringPrintf(INDENT "RecentQueue: length=%u\n", mRecentQueue.count()); - for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) { + if (!mRecentQueue.empty()) { + dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); + for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); dump += StringPrintf(", age=%0.1fms\n", @@ -3788,9 +3790,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } // Dump inbound events from oldest to newest. - if (!mInboundQueue.isEmpty()) { - dump += StringPrintf(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); - for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) { + if (!mInboundQueue.empty()) { + dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size()); + for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); dump += StringPrintf(", age=%0.1fms\n", @@ -4506,7 +4508,7 @@ void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventE void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { - ATRACE_INT("iq", mInboundQueue.count()); + ATRACE_INT("iq", mInboundQueue.size()); } } diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index d73c4d6dd7..b6c0797045 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -451,14 +451,6 @@ public: virtual status_t pilferPointers(const sp& token) override; private: - template - struct Link { - T* next; - T* prev; - - protected: - inline Link() : next(nullptr), prev(nullptr) { } - }; struct InjectionState { mutable int32_t refCount; @@ -476,7 +468,7 @@ private: ~InjectionState(); }; - struct EventEntry : Link { + struct EventEntry { enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, @@ -648,7 +640,7 @@ private: typedef std::function Command; class Connection; - struct CommandEntry : Link { + struct CommandEntry { explicit CommandEntry(Command command); ~CommandEntry(); @@ -668,75 +660,6 @@ private: sp newToken; }; - // Generic queue implementation. - template - struct Queue { - T* head; - T* tail; - uint32_t entryCount; - - inline Queue() : head(nullptr), tail(nullptr), entryCount(0) { - } - - inline bool isEmpty() const { - return !head; - } - - inline void enqueueAtTail(T* entry) { - entryCount++; - entry->prev = tail; - if (tail) { - tail->next = entry; - } else { - head = entry; - } - entry->next = nullptr; - tail = entry; - } - - inline void enqueueAtHead(T* entry) { - entryCount++; - entry->next = head; - if (head) { - head->prev = entry; - } else { - tail = entry; - } - entry->prev = nullptr; - head = entry; - } - - inline void dequeue(T* entry) { - entryCount--; - if (entry->prev) { - entry->prev->next = entry->next; - } else { - head = entry->next; - } - if (entry->next) { - entry->next->prev = entry->prev; - } else { - tail = entry->prev; - } - } - - inline T* dequeueAtHead() { - entryCount--; - T* entry = head; - head = entry->next; - if (head) { - head->prev = nullptr; - } else { - tail = nullptr; - } - return entry; - } - - uint32_t count() const { - return entryCount; - } - }; - /* Specifies which events are to be canceled and why. */ struct CancelationOptions { enum Mode { @@ -928,9 +851,9 @@ private: sp mLooper; EventEntry* mPendingEvent GUARDED_BY(mLock); - Queue mInboundQueue GUARDED_BY(mLock); - Queue mRecentQueue GUARDED_BY(mLock); - Queue mCommandQueue GUARDED_BY(mLock); + std::deque mInboundQueue GUARDED_BY(mLock); + std::deque mRecentQueue GUARDED_BY(mLock); + std::deque mCommandQueue GUARDED_BY(mLock); DropReason mLastDropReason GUARDED_BY(mLock); -- cgit v1.2.3-59-g8ed1b From e7c94b9a5aa3b0425779fa1bb4dd8d3b0c7a6b8b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 29 Jul 2019 09:17:54 -0700 Subject: Use unique_ptr for CommandEntry Currently, there is some unusual behaviour happening for CommandEntries. The entry is created and pushed onto a queue, but a pointer to the entry is returned in order to modify the entry after it has been created. Instead, fully construct the entry before enqueueing it. Also, switch to using unique_ptr in order to simplify memory management of queue entries. Bug: 70668286 Test: SANITIZE_TARGET=hwaddress atest libinput_tests inputflinger_tests Change-Id: Ie8ee9b732cdcd8f9a391f8315346fe4720d68e58 --- services/inputflinger/InputDispatcher.cpp | 49 +++++++++++++++++-------------- services/inputflinger/InputDispatcher.h | 4 +-- 2 files changed, 29 insertions(+), 24 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 656793a9ca..1eb979ef19 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -715,21 +715,18 @@ bool InputDispatcher::runCommandsLockedInterruptible() { } do { - CommandEntry* commandEntry = mCommandQueue.front(); + std::unique_ptr commandEntry = std::move(mCommandQueue.front()); mCommandQueue.pop_front(); Command command = commandEntry->command; - command(*this, commandEntry); // commands are implicitly 'LockedInterruptible' + command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible' commandEntry->connection.clear(); - delete commandEntry; } while (!mCommandQueue.empty()); return true; } -InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { - CommandEntry* commandEntry = new CommandEntry(command); - mCommandQueue.push_back(commandEntry); - return commandEntry; +void InputDispatcher::postCommandLocked(std::unique_ptr commandEntry) { + mCommandQueue.push_back(std::move(commandEntry)); } void InputDispatcher::drainInboundQueueLocked() { @@ -813,9 +810,10 @@ bool InputDispatcher::dispatchConfigurationChangedLocked( resetKeyRepeatLocked(); // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); commandEntry->eventTime = entry->eventTime; + postCommandLocked(std::move(commandEntry)); return true; } @@ -887,7 +885,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - CommandEntry* commandEntry = postCommandLocked( + std::unique_ptr commandEntry = std::make_unique( &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); @@ -896,6 +894,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, getInputChannelLocked(focusedWindowHandle->getToken()); } commandEntry->keyEntry = entry; + postCommandLocked(std::move(commandEntry)); entry->refCount += 1; return false; // wait for the command to run } else { @@ -1992,10 +1991,11 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } } - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible); + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doPokeUserActivityLockedInterruptible); commandEntry->eventTime = eventEntry->eventTime; commandEntry->userActivityEventType = eventType; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, @@ -2210,9 +2210,10 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a return; } - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -4095,12 +4096,13 @@ ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputC void InputDispatcher::onDispatchCycleFinishedLocked( nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible); + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onDispatchCycleBrokenLocked( @@ -4108,19 +4110,21 @@ void InputDispatcher::onDispatchCycleBrokenLocked( ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", connection->getInputChannelName().c_str()); - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); commandEntry->connection = connection; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onFocusChangedLocked(const sp& oldFocus, const sp& newFocus) { sp oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; sp newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible); + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyFocusChangedLockedInterruptible); commandEntry->oldToken = oldToken; commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onANRLocked( @@ -4150,12 +4154,13 @@ void InputDispatcher::onANRLocked( mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); dumpDispatchStateLocked(mLastANRState); - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible); + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; commandEntry->reason = reason; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible ( diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index b6c0797045..92e1e5feb7 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -853,7 +853,7 @@ private: EventEntry* mPendingEvent GUARDED_BY(mLock); std::deque mInboundQueue GUARDED_BY(mLock); std::deque mRecentQueue GUARDED_BY(mLock); - std::deque mCommandQueue GUARDED_BY(mLock); + std::deque> mCommandQueue GUARDED_BY(mLock); DropReason mLastDropReason GUARDED_BY(mLock); @@ -953,7 +953,7 @@ private: // Deferred command processing. bool haveCommandsLocked() const REQUIRES(mLock); bool runCommandsLockedInterruptible() REQUIRES(mLock); - CommandEntry* postCommandLocked(Command command) REQUIRES(mLock); + void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); -- cgit v1.2.3-59-g8ed1b From 2c9a3341977e315ffb8e78cac2343a2682e71dc3 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Tue, 23 Jul 2019 14:18:59 +0800 Subject: Add associated function to physical keyboard The function keys (HOME/BACK/DPAD...) may not always be virtual keys, so we need provide a static input mapping mechanism to the physical keyboard to make sure key events can dispatch to the corresponding display. - Find the associated viewport for keyboard when display info changed and has an associated display port specified. - Should disable the input device when an associated port specified but no display viewport found. - Changed getAssociatedDisplay -> getAssociatedDisplayId. - Add test cases to test input device and keyboard associated mapping. Bug: 137693244 Test: atest -a libinput_tests inputflinger_tests Change-Id: If218f55645a78a4b50cec92b35b0dbae641dd1a1 --- services/inputflinger/InputReader.cpp | 86 +++++++-- services/inputflinger/InputReader.h | 22 ++- services/inputflinger/tests/InputReader_test.cpp | 216 ++++++++++++++++++----- 3 files changed, 258 insertions(+), 66 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 4631becee8..1cbf78eb43 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -819,14 +819,19 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { } InputDevice* device = mDevices.valueAt(deviceIndex); - std::optional associatedDisplayId = device->getAssociatedDisplay(); + if (!device->isEnabled()) { + ALOGW("Ignoring disabled device %s", device->getName().c_str()); + return false; + } + + std::optional associatedDisplayId = device->getAssociatedDisplayId(); // No associated display. By default, can dispatch to all displays. if (!associatedDisplayId) { return true; } if (*associatedDisplayId == ADISPLAY_ID_NONE) { - ALOGW("Device has associated, but no associated display id."); + ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str()); return true; } @@ -1002,6 +1007,13 @@ bool InputDevice::isEnabled() { } void InputDevice::setEnabled(bool enabled, nsecs_t when) { + if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { + ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " + "but the corresponding viewport is not found", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + if (isEnabled() == enabled) { return; } @@ -1103,6 +1115,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { // In most situations, no port will be specified. mAssociatedDisplayPort = std::nullopt; + mAssociatedViewport = std::nullopt; // Find the display port that corresponds to the current input port. const std::string& inputPort = mIdentifier.location; if (!inputPort.empty()) { @@ -1112,6 +1125,23 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config mAssociatedDisplayPort = std::make_optional(displayPort->second); } } + + // If the device was explicitly disabled by the user, it would be present in the + // "disabledDevices" list. If it is associated with a specific display, and it was not + // explicitly disabled, then enable/disable the device based on whether we can find the + // corresponding viewport. + bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); + if (mAssociatedDisplayPort) { + mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); + if (!mAssociatedViewport) { + ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " + "but the corresponding viewport is not found.", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + } + + setEnabled(enabled, when); } for (InputMapper* mapper : mMappers) { @@ -1276,9 +1306,15 @@ void InputDevice::notifyReset(nsecs_t when) { mContext->getListener()->notifyDeviceReset(&args); } -std::optional InputDevice::getAssociatedDisplay() { +std::optional InputDevice::getAssociatedDisplayId() { + // Check if we had associated to the specific display. + if (mAssociatedViewport) { + return mAssociatedViewport->displayId; + } + + // No associated display port, check if some InputMapper is associated. for (InputMapper* mapper : mMappers) { - std::optional associatedDisplayId = mapper->getAssociatedDisplay(); + std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); if (associatedDisplayId) { return associatedDisplayId; } @@ -2229,6 +2265,22 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); } +std::optional KeyboardInputMapper::findViewport( + nsecs_t when, const InputReaderConfiguration* config) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // No associated display defined, try to find default display if orientationAware. + if (mParameters.orientationAware) { + return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return std::nullopt; +} + void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { InputMapper::configure(when, config, changes); @@ -2239,9 +2291,7 @@ void KeyboardInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware) { - mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } + mViewport = findViewport(when, config); } } @@ -2521,6 +2571,12 @@ void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, } } +std::optional KeyboardInputMapper::getAssociatedDisplayId() { + if (mViewport) { + return std::make_optional(mViewport->displayId); + } + return std::nullopt; +} // --- CursorInputMapper --- @@ -2935,7 +2991,7 @@ void CursorInputMapper::fadePointer() { } } -std::optional CursorInputMapper::getAssociatedDisplay() { +std::optional CursorInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { if (mParameters.mode == Parameters::MODE_POINTER) { return std::make_optional(mPointerController->getDisplayId()); @@ -3467,15 +3523,10 @@ std::optional TouchInputMapper::findViewport() { const std::optional displayPort = mDevice->getAssociatedDisplayPort(); if (displayPort) { // Find the viewport that contains the same port - std::optional v = mConfig.getDisplayViewportByPort(*displayPort); - if (!v) { - ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " - "but the corresponding viewport is not found.", - getDeviceName().c_str(), *displayPort); - } - return v; + return mDevice->getAssociatedViewport(); } + // Check if uniqueDisplayId is specified in idc file. if (!mParameters.uniqueDisplayId.empty()) { return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); } @@ -3499,6 +3550,7 @@ std::optional TouchInputMapper::findViewport() { return viewport; } + // No associated display, return a non-display viewport. DisplayViewport newViewport; // Raw width and height in the natural orientation. int32_t rawWidth = mRawPointerAxes.getRawWidth(); @@ -6508,7 +6560,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 if (mDeviceMode == DEVICE_MODE_POINTER) { mPointerController->getPosition(&xCursorPosition, &yCursorPosition); } - const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); + const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); std::for_each(frames.begin(), frames.end(), @@ -6817,7 +6869,7 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode return true; } -std::optional TouchInputMapper::getAssociatedDisplay() { +std::optional TouchInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { if (mDeviceMode == DEVICE_MODE_POINTER) { return std::make_optional(mPointerController->getDisplayId()); diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index b7f94c18f3..0666ca54bf 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -265,7 +265,9 @@ public: inline std::optional getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } - + inline std::optional getAssociatedViewport() const { + return mAssociatedViewport; + } inline void setMic(bool hasMic) { mHasMic = hasMic; } inline bool hasMic() const { return mHasMic; } @@ -324,7 +326,8 @@ public: return value; } - std::optional getAssociatedDisplay(); + std::optional getAssociatedDisplayId(); + private: InputReaderContext* mContext; int32_t mId; @@ -339,6 +342,7 @@ private: uint32_t mSources; bool mIsExternal; std::optional mAssociatedDisplayPort; + std::optional mAssociatedViewport; bool mHasMic; bool mDropUntilNextSync; @@ -718,9 +722,8 @@ public: virtual void updateExternalStylusState(const StylusState& state); virtual void fadePointer(); - virtual std::optional getAssociatedDisplay() { - return std::nullopt; - } + virtual std::optional getAssociatedDisplayId() { return std::nullopt; } + protected: InputDevice* mDevice; InputReaderContext* mContext; @@ -802,6 +805,7 @@ public: virtual int32_t getMetaState(); virtual void updateMetaState(int32_t keyCode); + virtual std::optional getAssociatedDisplayId(); private: // The current viewport. @@ -855,6 +859,8 @@ private: void updateLedState(bool reset); void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); + std::optional findViewport(nsecs_t when, + const InputReaderConfiguration* config); }; @@ -874,7 +880,8 @@ public: virtual void fadePointer(); - virtual std::optional getAssociatedDisplay(); + virtual std::optional getAssociatedDisplayId(); + private: // Amount that trackball needs to move in order to generate a key event. static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; @@ -968,7 +975,8 @@ public: virtual void cancelTouch(nsecs_t when); virtual void timeoutExpired(nsecs_t when); virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional getAssociatedDisplay(); + virtual std::optional getAssociatedDisplayId(); + protected: CursorButtonAccumulator mCursorButtonAccumulator; CursorScrollAccumulator mCursorScrollAccumulator; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d95ac96124..348a12bf3a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1556,10 +1556,14 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); - // Check device. + // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID)); ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); + + // Can't dispatch event from a disabled device. + disableDevice(deviceId, device); + ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1568,6 +1572,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { class InputDeviceTest : public testing::Test { protected: static const char* DEVICE_NAME; + static const char* DEVICE_LOCATION; static const int32_t DEVICE_ID; static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; @@ -1589,6 +1594,7 @@ protected: mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; + identifier.location = DEVICE_LOCATION; mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } @@ -1603,6 +1609,7 @@ protected: }; const char* InputDeviceTest::DEVICE_NAME = "device"; +const char* InputDeviceTest::DEVICE_LOCATION = "USB1"; const int32_t InputDeviceTest::DEVICE_ID = 1; const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; @@ -1755,6 +1762,49 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); } +// A single input device is associated with a specific display. Check that: +// 1. Device is disabled if the viewport corresponding to the associated display is not found +// 2. Device is disabled when setEnabled API is called +TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { + FakeInputMapper* mapper = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); + mDevice->addMapper(mapper); + + // First Configuration. + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + + // Device should be enabled by default. + ASSERT_TRUE(mDevice->isEnabled()); + + // Prepare associated info. + constexpr uint8_t hdmi = 1; + const std::string UNIQUE_ID = "local:1"; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + // Device should be disabled because it is associated with a specific display via + // input port <-> display port association, but the corresponding display is not found + ASSERT_FALSE(mDevice->isEnabled()); + + // Prepare displays. + mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, + ViewportType::VIEWPORT_INTERNAL); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_TRUE(mDevice->isEnabled()); + + // Device should be disabled after set disable. + mFakePolicy->addDisabledDevice(mDevice->getId()); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_ENABLED_STATE); + ASSERT_FALSE(mDevice->isEnabled()); + + // Device should still be disabled even found the associated display. + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_FALSE(mDevice->isEnabled()); +} // --- InputMapperTest --- @@ -1926,8 +1976,9 @@ protected: void prepareDisplay(int32_t orientation); - void testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode); + void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode, + int32_t originalKeyCode, int32_t rotatedKeyCode, + int32_t displayId = ADISPLAY_ID_NONE); }; /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the @@ -1939,7 +1990,8 @@ void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { } void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) { + int32_t originalScanCode, int32_t originalKeyCode, + int32_t rotatedKeyCode, int32_t displayId) { NotifyKeyArgs args; process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1); @@ -1947,15 +1999,16 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); ASSERT_EQ(originalScanCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); ASSERT_EQ(originalScanCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); } - TEST_F(KeyboardInputMapperTest, GetSources) { KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); @@ -2136,47 +2189,47 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { addMapperAndConfigure(mapper); prepareDisplay(DISPLAY_ORIENTATION_0); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_180); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_UP, DISPLAY_ID)); // Special case: if orientation changes while key is down, we still emit the same keycode // in the key up as we did in the key down. @@ -2360,6 +2413,84 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); } +TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { + // keyboard 1. + mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + // keyboard 2. + const std::string USB2 = "USB2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + InputDeviceIdentifier identifier; + identifier.name = "KEYBOARD2"; + identifier.location = USB2; + std::unique_ptr device2 = + std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, + DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addMapperAndConfigure(mapper); + + KeyboardInputMapper* mapper2 = new KeyboardInputMapper(device2.get(), AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + device2->addMapper(mapper2); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); + device2->reset(ARBITRARY_TIME); + + // Prepared displays and associated info. + constexpr uint8_t hdmi1 = 0; + constexpr uint8_t hdmi2 = 1; + const std::string SECONDARY_UNIQUE_ID = "local:1"; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); + mFakePolicy->addInputPortAssociation(USB2, hdmi2); + + // No associated display viewport found, should disable the device. + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_FALSE(device2->isEnabled()); + + // Prepare second display. + constexpr int32_t newDisplayId = 2; + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL); + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL); + // Default device will reconfigure above, need additional reconfiguration for another device. + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + // Device should be enabled after the associated display is found. + ASSERT_TRUE(mDevice->isEnabled()); + ASSERT_TRUE(device2->isEnabled()); + + // Test pad key events + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, newDisplayId)); +} // --- CursorInputMapperTest --- @@ -6276,12 +6407,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; - const int32_t SECOND_DEVICE_ID = 2; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; InputDeviceIdentifier identifier; - identifier.name = DEVICE_NAME; + identifier.name = "TOUCHSCREEN2"; identifier.location = USB2; - InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + std::unique_ptr device2 = + std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, + DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0 /*flat*/, 0 /*fuzz*/); @@ -6296,7 +6428,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { String8("touchScreen")); // Setup the second touch screen device. - MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2); + MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2.get()); device2->addMapper(mapper2); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); -- cgit v1.2.3-59-g8ed1b From 9da14733afb5da5cb362f3c865d85d303cc62b15 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Mon, 2 Sep 2019 16:16:58 +0800 Subject: Fix touch can't work after enabled the associated viewport If a device has associated port but there is no viewport found, it would be expeced to be disabled. But for some devices, such as touch screen or joystick, they have to read the axis ranges info in InputMapper at first time configuration changed. So we have to defer disabling the device after info has been read when the device plugged. Bug: 140205788 Test: atest inputflinger_tests Change-Id: I9dd55e0016b6a020aab211dada45880195aec8dd --- services/inputflinger/InputReader.cpp | 13 +++++++++- services/inputflinger/tests/InputReader_test.cpp | 33 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 1cbf78eb43..d59f274ab0 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -1141,13 +1141,24 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } } - setEnabled(enabled, when); + if (changes) { + // For first-time configuration, only allow device to be disabled after mappers have + // finished configuring. This is because we need to read some of the properties from + // the device's open fd. + setEnabled(enabled, when); + } } for (InputMapper* mapper : mMappers) { mapper->configure(when, config, changes); mSources |= mapper->getSources(); } + + // If a device is just plugged but it might be disabled, we need to update some info like + // axis range of touch from each InputMapper first, then disable it. + if (!changes) { + setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); + } } } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 348a12bf3a..09df2a70c7 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -527,7 +527,7 @@ private: virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const { Device* device = getDevice(deviceId); - if (device) { + if (device && device->enabled) { ssize_t index = device->absoluteAxes.indexOfKey(axis); if (index >= 0) { *outAxisInfo = device->absoluteAxes.valueAt(index); @@ -6554,4 +6554,35 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { ASSERT_EQ(frames, motionArgs.videoFrames); } +/** + * If we had defined port associations, but the viewport is not ready, the touch device would be + * expected to be disabled, and it should be enabled after the viewport has found. + */ +TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + constexpr uint8_t hdmi2 = 1; + const std::string secondaryUniqueId = "uniqueId2"; + constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2); + + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + ASSERT_EQ(mDevice->isEnabled(), false); + + // Add display on hdmi2, the device should be enabled and can receive touch event. + prepareSecondaryDisplay(type, hdmi2); + ASSERT_EQ(mDevice->isEnabled(), true); + + // Send a touch event. + processPosition(mapper, 100, 100); + processSync(mapper); + + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 146ecfd99c77fac65597648726f4567100b96d9a Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 29 Jul 2019 16:04:31 -0700 Subject: Use unordered_map for connections Currently, keyedvector is used for keeping track of connections by file descriptors. Instead, use supported api std::unordered_map. Also, no longer remove raw indices into the map, because that's implementation detail of map that should not be user-facing. Bug: 70668286 Test: SANITIZE_TARGET=hwaddress atest libinput_tests inputflinger_tests Change-Id: Id31b0002a7ff2310dcc506f834722b1e69a05571 --- services/inputflinger/InputDispatcher.cpp | 145 ++++++++++++++++-------------- services/inputflinger/InputDispatcher.h | 4 +- 2 files changed, 80 insertions(+), 69 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 1eb979ef19..c516ab7eed 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -262,12 +262,32 @@ static void dumpRegion(std::string& dump, const Region& region) { * if the entry is not found. * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. */ -template -static T getValueByKey(const std::unordered_map& map, U key) { +template +static V getValueByKey(const std::unordered_map& map, K key) { auto it = map.find(key); - return it != map.end() ? it->second : T{}; + return it != map.end() ? it->second : V{}; } +/** + * Find the entry in std::unordered_map by value, and remove it. + * If more than one entry has the same value, then all matching + * key-value pairs will be removed. + * + * Return true if at least one value has been removed. + */ +template +static bool removeByValue(std::unordered_map& map, const V& value) { + bool removed = false; + for (auto it = map.begin(); it != map.end();) { + if (it->second == value) { + it = map.erase(it); + removed = true; + } else { + it++; + } + } + return removed; +} // --- InputDispatcher --- @@ -300,8 +320,9 @@ InputDispatcher::~InputDispatcher() { drainInboundQueueLocked(); } - while (mConnectionsByFd.size() != 0) { - unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel); + while (!mConnectionsByFd.empty()) { + sp connection = mConnectionsByFd.begin()->second; + unregisterInputChannel(connection->inputChannel); } } @@ -1072,9 +1093,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, pokeUserActivityLocked(eventEntry); for (const InputTarget& inputTarget : inputTargets) { - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp connection = mConnectionsByFd.valueAt(connectionIndex); + sp connection = getConnectionLocked(inputTarget.inputChannel); + if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { #if DEBUG_FOCUS @@ -1172,21 +1192,18 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout mInputTargetWaitTimeoutExpired = true; // Input state will not be realistic. Mark it out of sync. - if (inputChannel.get()) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex >= 0) { - sp connection = mConnectionsByFd.valueAt(connectionIndex); - sp token = connection->inputChannel->getToken(); - - if (token != nullptr) { - removeWindowByTokenLocked(token); - } + sp connection = getConnectionLocked(inputChannel); + if (connection != nullptr) { + sp token = connection->inputChannel->getToken(); - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } + if (token != nullptr) { + removeWindowByTokenLocked(token); + } + + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } } @@ -1857,16 +1874,15 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT } // If the window's connection is not registered then keep waiting. - ssize_t connectionIndex = getConnectionIndexLocked( - getInputChannelLocked(windowHandle->getToken())); - if (connectionIndex < 0) { + sp connection = + getConnectionLocked(getInputChannelLocked(windowHandle->getToken())); + if (connection == nullptr) { return StringPrintf("Waiting because the %s window's input channel is not " "registered with the input dispatcher. The window may be in the process " "of being removed.", targetType); } // If the connection is dead then keep waiting. - sp connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->status != Connection::STATUS_NORMAL) { return StringPrintf("Waiting because the %s window's input connection is %s." "The window may be in the process of being removed.", targetType, @@ -2410,15 +2426,14 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { { // acquire lock std::scoped_lock _l(d->mLock); - ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); - if (connectionIndex < 0) { + if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) { ALOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", fd, events); return 0; // remove the callback } bool notify; - sp connection = d->mConnectionsByFd.valueAt(connectionIndex); + sp connection = d->mConnectionsByFd[fd]; if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " @@ -2470,9 +2485,8 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked ( const CancelationOptions& options) { - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByFd.valueAt(i), options); + for (const auto& pair : mConnectionsByFd) { + synthesizeCancelationEventsForConnectionLocked(pair.second, options); } } @@ -2495,11 +2509,12 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp& channel, const CancelationOptions& options) { - ssize_t index = getConnectionIndexLocked(channel); - if (index >= 0) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByFd.valueAt(index), options); + sp connection = getConnectionLocked(channel); + if (connection == nullptr) { + return; } + + synthesizeCancelationEventsForConnectionLocked(connection, options); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( @@ -3586,15 +3601,11 @@ Found: return false; } - sp fromChannel = getInputChannelLocked(fromToken); sp toChannel = getInputChannelLocked(toToken); - ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel); - ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel); - if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) { - sp fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex); - sp toConnection = mConnectionsByFd.valueAt(toConnectionIndex); - + sp fromConnection = getConnectionLocked(fromChannel); + sp toConnection = getConnectionLocked(toChannel); + if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.copyPointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); @@ -3815,16 +3826,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "ReplacedKeys: \n"; } - if (!mConnectionsByFd.isEmpty()) { + if (!mConnectionsByFd.empty()) { dump += INDENT "Connections:\n"; - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - const sp& connection = mConnectionsByFd.valueAt(i); - dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", - i, connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), - connection->getStatusLabel(), toString(connection->monitor), - toString(connection->inputPublisherBlocked)); + for (const auto& pair : mConnectionsByFd) { + const sp& connection = pair.second; + dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " + "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + pair.first, connection->getInputChannelName().c_str(), + connection->getWindowName().c_str(), connection->getStatusLabel(), + toString(connection->monitor), + toString(connection->inputPublisherBlocked)); if (!connection->outboundQueue.empty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", @@ -3893,8 +3904,8 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan { // acquire lock std::scoped_lock _l(mLock); - - if (getConnectionIndexLocked(inputChannel) >= 0) { + sp existingConnection = getConnectionLocked(inputChannel); + if (existingConnection != nullptr) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); return BAD_VALUE; @@ -3903,7 +3914,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan sp connection = new Connection(inputChannel, false /*monitor*/); int fd = inputChannel->getFd(); - mConnectionsByFd.add(fd, connection); + mConnectionsByFd[fd] = connection; mInputChannelsByToken[inputChannel->getToken()] = inputChannel; mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); @@ -3932,7 +3943,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan sp connection = new Connection(inputChannel, true /*monitor*/); const int fd = inputChannel->getFd(); - mConnectionsByFd.add(fd, connection); + mConnectionsByFd[fd] = connection; mInputChannelsByToken[inputChannel->getToken()] = inputChannel; auto& monitorsByDisplay = isGestureMonitor @@ -3970,16 +3981,15 @@ status_t InputDispatcher::unregisterInputChannel(const sp& inputCh status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, bool notify) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex < 0) { + sp connection = getConnectionLocked(inputChannel); + if (connection == nullptr) { ALOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().c_str()); return BAD_VALUE; } - sp connection = mConnectionsByFd.valueAt(connectionIndex); - mConnectionsByFd.removeItemsAt(connectionIndex); - + const bool removed = removeByValue(mConnectionsByFd, connection); + ALOG_ASSERT(removed); mInputChannelsByToken.erase(inputChannel->getToken()); if (connection->monitor) { @@ -4079,19 +4089,20 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputChannel) { +sp InputDispatcher::getConnectionLocked( + const sp& inputChannel) { if (inputChannel == nullptr) { - return -1; + return nullptr; } - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - sp connection = mConnectionsByFd.valueAt(i); + for (const auto& pair : mConnectionsByFd) { + sp connection = pair.second; if (connection->inputChannel->getToken() == inputChannel->getToken()) { - return i; + return connection; } } - return -1; + return nullptr; } void InputDispatcher::onDispatchCycleFinishedLocked( diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 92e1e5feb7..e35ba2756b 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -887,7 +887,7 @@ private: bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock); // All registered connections mapped by channel file descriptor. - KeyedVector > mConnectionsByFd GUARDED_BY(mLock); + std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); struct IBinderHash { std::size_t operator()(const sp& b) const { @@ -901,7 +901,7 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - ssize_t getConnectionIndexLocked(const sp& inputChannel) REQUIRES(mLock); + sp getConnectionLocked(const sp& inputChannel) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay -- cgit v1.2.3-59-g8ed1b From 6cbb97591535868d629a562dfd7d8e24865cf551 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 5 Sep 2019 16:38:18 +0800 Subject: Revert "Fix drag and drop (2/3)" This reverts commit fbe5d9c4233dcd802a122f70ca5a3641fcd556ca. Bug: 137819199 Test: manual Change-Id: I7afec569519b9c69eb03225672db6db141b20241 --- include/input/IInputFlinger.h | 4 +-- libs/gui/tests/EndToEndNativeInputTest.cpp | 50 ------------------------------ libs/input/IInputFlinger.cpp | 17 ---------- services/inputflinger/InputManager.cpp | 4 --- services/inputflinger/InputManager.h | 1 - services/inputflinger/host/InputFlinger.h | 1 - services/surfaceflinger/SurfaceFlinger.cpp | 15 +-------- services/surfaceflinger/SurfaceFlinger.h | 1 - 8 files changed, 2 insertions(+), 91 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 4365a3c4e3..d23e3b7767 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -37,7 +37,6 @@ public: virtual void setInputWindows(const std::vector& inputHandles, const sp& setInputWindowsListener) = 0; - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; virtual void registerInputChannel(const sp& channel) = 0; virtual void unregisterInputChannel(const sp& channel) = 0; }; @@ -51,8 +50,7 @@ public: enum { SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_INPUT_CHANNEL_TRANSACTION, - UNREGISTER_INPUT_CHANNEL_TRANSACTION, - TRANSFER_TOUCH_FOCUS + UNREGISTER_INPUT_CHANNEL_TRANSACTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 386f731d23..f158e566e6 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -133,27 +133,6 @@ public: EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } - void expectMotionEvent(int motionEventType, int x, int y) { - InputEvent *ev = consumeEvent(); - ASSERT_NE(ev, nullptr); - ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION); - MotionEvent *mev = static_cast(ev); - EXPECT_EQ(motionEventType, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); - } - - void expectNoMotionEvent(int motionEventType) { - InputEvent *ev = consumeEvent(); - if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) { - // Didn't find an event or a motion event so assume action didn't occur. - return; - } - - MotionEvent *mev = static_cast(ev); - EXPECT_NE(motionEventType, mev->getAction()); - } - ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel); } @@ -278,15 +257,6 @@ void injectTap(int x, int y) { } } -void injectMotionEvent(std::string event, int x, int y) { - char *buf1, *buf2; - asprintf(&buf1, "%d", x); - asprintf(&buf2, "%d", y); - if (fork() == 0) { - execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL); - } -} - TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -504,26 +474,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { bgSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, transfer_touch_focus) { - std::unique_ptr fromSurface = makeSurface(100, 100); - - fromSurface->showAt(10, 10); - injectMotionEvent("DOWN", 11, 11); - fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1); - - std::unique_ptr toSurface = makeSurface(100, 100); - toSurface->showAt(10, 10); - - sp fromToken = fromSurface->mServerChannel->getToken(); - sp toToken = toSurface->mServerChannel->getToken(); - SurfaceComposerClient::Transaction t; - t.transferTouchFocus(fromToken, toToken).apply(true); - - injectMotionEvent("UP", 11, 11); - toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1); - fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP); -} - TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(-1, -1); diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 90f6e09a69..8ec51653a8 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -45,16 +45,6 @@ public: IBinder::FLAG_ONEWAY); } - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeStrongBinder(fromToken); - data.writeStrongBinder(toToken); - remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply, - IBinder::FLAG_ONEWAY); - } - virtual void registerInputChannel(const sp& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); @@ -102,13 +92,6 @@ status_t BnInputFlinger::onTransact( unregisterInputChannel(channel); break; } - case TRANSFER_TOUCH_FOCUS: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp fromToken = data.readStrongBinder(); - sp toToken = data.readStrongBinder(); - transferTouchFocus(fromToken, toToken); - break; - } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 3996cca646..acb53be69e 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -117,10 +117,6 @@ void InputManager::setInputWindows(const std::vector& infos, } } -void InputManager::transferTouchFocus(const sp& fromToken, const sp& toToken) { - mDispatcher->transferTouchFocus(fromToken, toToken); -} - // Used by tests only. void InputManager::registerInputChannel(const sp& channel) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e568df54df..32f7ae0058 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -96,7 +96,6 @@ public: virtual void setInputWindows(const std::vector& handles, const sp& setInputWindowsListener); - virtual void transferTouchFocus(const sp& fromToken, const sp& toToken); virtual void registerInputChannel(const sp& channel); virtual void unregisterInputChannel(const sp& channel); diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index d8b352cbc6..973b4f92fa 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -42,7 +42,6 @@ public: virtual status_t dump(int fd, const Vector& args); void setInputWindows(const std::vector&, const sp&) {} - void transferTouchFocus(const sp&, const sp&) {} void registerInputChannel(const sp&) {} void unregisterInputChannel(const sp&) {} diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3498419cdf..a35426e8d4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2606,7 +2606,7 @@ void SurfaceFlinger::updateInputFlinger() { setInputWindowsFinished(); } - executeInputWindowCommands(); + mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { @@ -2630,19 +2630,6 @@ void SurfaceFlinger::commitInputWindowCommands() { mPendingInputWindowCommands.clear(); } -void SurfaceFlinger::executeInputWindowCommands() { - for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) { - if (transferTouchFocusCommand.fromToken != nullptr && - transferTouchFocusCommand.toToken != nullptr && - transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) { - mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken, - transferTouchFocusCommand.toToken); - } - } - - mInputWindowCommands.clear(); -} - void SurfaceFlinger::updateCursorAsync() { compositionengine::CompositionRefreshArgs refreshArgs; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f220c26bdf..e0259c80ec 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -551,7 +551,6 @@ private: void updateInputFlinger(); void updateInputWindowInfo(); void commitInputWindowCommands() REQUIRES(mStateLock); - void executeInputWindowCommands(); void setInputWindowsFinished(); void updateCursorAsync(); -- cgit v1.2.3-59-g8ed1b From 0fc2fa7e6b85d90f449ba8ba843784d1284db862 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 29 Aug 2019 17:22:15 -0700 Subject: Move InputDispatcher code into a folder. Note this is an intermediate stage so that the following change won't ruin git history. Should never let the HEAD be this commit. Bug: 140139676 Test: Builds. Change-Id: I263929e2d2dd17858eaea69121564335bcc2ef1c --- services/inputflinger/Android.bp | 3 +- services/inputflinger/InputDispatcher.cpp | 5362 -------------------- services/inputflinger/InputDispatcher.h | 1240 ----- .../inputflinger/dispatcher/InputDispatcher.cpp | 5359 +++++++++++++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 1227 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 2 +- 6 files changed, 6589 insertions(+), 6604 deletions(-) delete mode 100644 services/inputflinger/InputDispatcher.cpp delete mode 100644 services/inputflinger/InputDispatcher.h create mode 100644 services/inputflinger/dispatcher/InputDispatcher.cpp create mode 100644 services/inputflinger/dispatcher/InputDispatcher.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index bdee6fe043..8649c1ebc6 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -30,7 +30,7 @@ cc_library_shared { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", - "InputDispatcher.cpp", + "dispatcher/InputDispatcher.cpp", "InputManager.cpp", ], @@ -58,6 +58,7 @@ cc_library_shared { export_include_dirs: [ ".", "include", + "dispatcher", ], } diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp deleted file mode 100644 index c516ab7eed..0000000000 --- a/services/inputflinger/InputDispatcher.cpp +++ /dev/null @@ -1,5362 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputDispatcher" -#define ATRACE_TAG ATRACE_TAG_INPUT - -#define LOG_NDEBUG 0 - -// Log detailed debug messages about each inbound event notification to the dispatcher. -#define DEBUG_INBOUND_EVENT_DETAILS 0 - -// Log detailed debug messages about each outbound event processed by the dispatcher. -#define DEBUG_OUTBOUND_EVENT_DETAILS 0 - -// Log debug messages about the dispatch cycle. -#define DEBUG_DISPATCH_CYCLE 0 - -// Log debug messages about registrations. -#define DEBUG_REGISTRATION 0 - -// Log debug messages about input event injection. -#define DEBUG_INJECTION 0 - -// Log debug messages about input focus tracking. -#define DEBUG_FOCUS 0 - -// Log debug messages about the app switch latency optimization. -#define DEBUG_APP_SWITCH 0 - -// Log debug messages about hover events. -#define DEBUG_HOVER 0 - -#include "InputDispatcher.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " - -using android::base::StringPrintf; - -namespace android { - -// Default input dispatching timeout if there is no focused application or paused window -// from which to determine an appropriate dispatching timeout. -constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec - -// Amount of time to allow for all pending events to be processed when an app switch -// key is on the way. This is used to preempt input dispatch and drop input events -// when an application takes too long to respond and the user has pressed an app switch key. -constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec - -// Amount of time to allow for an event to be dispatched (measured since its eventTime) -// before considering it stale and dropping it. -constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec - -// Amount of time to allow touch events to be streamed out to a connection before requiring -// that the first event be finished. This value extends the ANR timeout by the specified -// amount. For example, if streaming is allowed to get ahead by one second relative to the -// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. -constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec - -// Log a warning when an event takes longer than this to process, even if an ANR does not occur. -constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec - -// Log a warning when an interception call takes longer than this to process. -constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; - -// Number of recent events to keep for debugging purposes. -constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; - -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - - -static inline nsecs_t now() { - return systemTime(SYSTEM_TIME_MONOTONIC); -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static std::string motionActionToString(int32_t action) { - // Convert MotionEvent action to string - switch(action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; - case AMOTION_EVENT_ACTION_UP: - return "UP"; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; - case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string keyActionToString(int32_t action) { - // Convert KeyEvent action to string - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - return "DOWN"; - case AKEY_EVENT_ACTION_UP: - return "UP"; - case AKEY_EVENT_ACTION_MULTIPLE: - return "MULTIPLE"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string dispatchModeToString(int32_t dispatchMode) { - switch (dispatchMode) { - case InputTarget::FLAG_DISPATCH_AS_IS: - return "DISPATCH_AS_IS"; - case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: - return "DISPATCH_AS_OUTSIDE"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: - return "DISPATCH_AS_HOVER_ENTER"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: - return "DISPATCH_AS_HOVER_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: - return "DISPATCH_AS_SLIPPERY_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: - return "DISPATCH_AS_SLIPPERY_ENTER"; - } - return StringPrintf("%" PRId32, dispatchMode); -} - -static inline int32_t getMotionEventActionPointerIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - -static bool isValidKeyAction(int32_t action) { - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - case AKEY_EVENT_ACTION_UP: - return true; - default: - return false; - } -} - -static bool validateKeyEvent(int32_t action) { - if (! isValidKeyAction(action)) { - ALOGE("Key event has invalid action code 0x%x", action); - return false; - } - return true; -} - -static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_OUTSIDE: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - case AMOTION_EVENT_ACTION_SCROLL: - return true; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t index = getMotionEventActionPointerIndex(action); - return index >= 0 && index < pointerCount; - } - case AMOTION_EVENT_ACTION_BUTTON_PRESS: - case AMOTION_EVENT_ACTION_BUTTON_RELEASE: - return actionButton != 0; - default: - return false; - } -} - -static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, - const PointerProperties* pointerProperties) { - if (! isValidMotionAction(action, actionButton, pointerCount)) { - ALOGE("Motion event has invalid action code 0x%x", action); - return false; - } - if (pointerCount < 1 || pointerCount > MAX_POINTERS) { - ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.", - pointerCount, MAX_POINTERS); - return false; - } - BitSet32 pointerIdBits; - for (size_t i = 0; i < pointerCount; i++) { - int32_t id = pointerProperties[i].id; - if (id < 0 || id > MAX_POINTER_ID) { - ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", - id, MAX_POINTER_ID); - return false; - } - if (pointerIdBits.hasBit(id)) { - ALOGE("Motion event has duplicate pointer id %d", id); - return false; - } - pointerIdBits.markBit(id); - } - return true; -} - -static void dumpRegion(std::string& dump, const Region& region) { - if (region.isEmpty()) { - dump += ""; - return; - } - - bool first = true; - Region::const_iterator cur = region.begin(); - Region::const_iterator const tail = region.end(); - while (cur != tail) { - if (first) { - first = false; - } else { - dump += "|"; - } - dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom); - cur++; - } -} - -/** - * Find the entry in std::unordered_map by key, and return it. - * If the entry is not found, return a default constructed entry. - * - * Useful when the entries are vectors, since an empty vector will be returned - * if the entry is not found. - * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. - */ -template -static V getValueByKey(const std::unordered_map& map, K key) { - auto it = map.find(key); - return it != map.end() ? it->second : V{}; -} - -/** - * Find the entry in std::unordered_map by value, and remove it. - * If more than one entry has the same value, then all matching - * key-value pairs will be removed. - * - * Return true if at least one value has been removed. - */ -template -static bool removeByValue(std::unordered_map& map, const V& value) { - bool removed = false; - for (auto it = map.begin(); it != map.end();) { - if (it->second == value) { - it = map.erase(it); - removed = true; - } else { - it++; - } - } - return removed; -} - -// --- InputDispatcher --- - -InputDispatcher::InputDispatcher(const sp& policy) - : mPolicy(policy), - mPendingEvent(nullptr), - mLastDropReason(DROP_REASON_NOT_DROPPED), - mAppSwitchSawKeyDown(false), - mAppSwitchDueTime(LONG_LONG_MAX), - mNextUnblockedEvent(nullptr), - mDispatchEnabled(false), - mDispatchFrozen(false), - mInputFilterEnabled(false), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { - mLooper = new Looper(false); - mReporter = createInputReporter(); - - mKeyRepeatState.lastKeyEntry = nullptr; - - policy->getDispatcherConfiguration(&mConfig); -} - -InputDispatcher::~InputDispatcher() { - { // acquire lock - std::scoped_lock _l(mLock); - - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - } - - while (!mConnectionsByFd.empty()) { - sp connection = mConnectionsByFd.begin()->second; - unregisterInputChannel(connection->inputChannel); - } -} - -void InputDispatcher::dispatchOnce() { - nsecs_t nextWakeupTime = LONG_LONG_MAX; - { // acquire lock - std::scoped_lock _l(mLock); - mDispatcherIsAlive.notify_all(); - - // Run a dispatch loop if there are no pending commands. - // The dispatch loop might enqueue commands to run afterwards. - if (!haveCommandsLocked()) { - dispatchOnceInnerLocked(&nextWakeupTime); - } - - // Run all pending commands if there are any. - // If any commands were run then force the next poll to wake up immediately. - if (runCommandsLockedInterruptible()) { - nextWakeupTime = LONG_LONG_MIN; - } - } // release lock - - // Wait for callback or timeout or wake. (make sure we round up, not down) - nsecs_t currentTime = now(); - int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); - mLooper->pollOnce(timeoutMillis); -} - -void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { - nsecs_t currentTime = now(); - - // Reset the key repeat timer whenever normal dispatch is suspended while the - // device is in a non-interactive state. This is to ensure that we abort a key - // repeat if the device is just coming out of sleep. - if (!mDispatchEnabled) { - resetKeyRepeatLocked(); - } - - // If dispatching is frozen, do not process timeouts or try to deliver any new events. - if (mDispatchFrozen) { -#if DEBUG_FOCUS - ALOGD("Dispatch frozen. Waiting some more."); -#endif - return; - } - - // Optimize latency of app switches. - // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has - // been pressed. When it expires, we preempt dispatch and drop all other pending events. - bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; - } - - // Ready to start a new event. - // If we don't already have a pending event, go grab one. - if (! mPendingEvent) { - if (mInboundQueue.empty()) { - if (isAppSwitchDue) { - // The inbound queue is empty so the app switch key we were waiting - // for will never arrive. Stop waiting for it. - resetPendingAppSwitchLocked(false); - isAppSwitchDue = false; - } - - // Synthesize a key repeat if appropriate. - if (mKeyRepeatState.lastKeyEntry) { - if (currentTime >= mKeyRepeatState.nextRepeatTime) { - mPendingEvent = synthesizeKeyRepeatLocked(currentTime); - } else { - if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { - *nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } - } - } - - // Nothing to do if there is no pending event. - if (!mPendingEvent) { - return; - } - } else { - // Inbound queue has at least one entry. - mPendingEvent = mInboundQueue.front(); - mInboundQueue.pop_front(); - traceInboundQueueLengthLocked(); - } - - // Poke user activity for this event. - if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { - pokeUserActivityLocked(mPendingEvent); - } - - // Get ready to dispatch the event. - resetANRTimeoutsLocked(); - } - - // Now we have an event to dispatch. - // All events are eventually dequeued and processed this way, even if we intend to drop them. - ALOG_ASSERT(mPendingEvent != nullptr); - bool done = false; - DropReason dropReason = DROP_REASON_NOT_DROPPED; - if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { - dropReason = DROP_REASON_POLICY; - } else if (!mDispatchEnabled) { - dropReason = DROP_REASON_DISABLED; - } - - if (mNextUnblockedEvent == mPendingEvent) { - mNextUnblockedEvent = nullptr; - } - - switch (mPendingEvent->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast(mPendingEvent); - done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped - break; - } - - case EventEntry::TYPE_DEVICE_RESET: { - DeviceResetEntry* typedEntry = - static_cast(mPendingEvent); - done = dispatchDeviceResetLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped - break; - } - - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(typedEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DROP_REASON_NOT_DROPPED) { - dropReason = DROP_REASON_APP_SWITCH; - } - } - if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; - } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; - } - done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast(mPendingEvent); - if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { - dropReason = DROP_REASON_APP_SWITCH; - } - if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; - } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; - } - done = dispatchMotionLocked(currentTime, typedEntry, - &dropReason, nextWakeupTime); - break; - } - - default: - ALOG_ASSERT(false); - break; - } - - if (done) { - if (dropReason != DROP_REASON_NOT_DROPPED) { - dropInboundEventLocked(mPendingEvent, dropReason); - } - mLastDropReason = dropReason; - - releasePendingEventLocked(); - *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately - } -} - -bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { - bool needWake = mInboundQueue.empty(); - mInboundQueue.push_back(entry); - traceInboundQueueLengthLocked(); - - switch (entry->type) { - case EventEntry::TYPE_KEY: { - // Optimize app switch latency. - // If the application takes too long to catch up then we drop all events preceding - // the app switch key. - KeyEntry* keyEntry = static_cast(entry); - if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { -#if DEBUG_APP_SWITCH - ALOGD("App switch is pending!"); -#endif - mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; - } - } - } - break; - } - - case EventEntry::TYPE_MOTION: { - // Optimize case where the current application is unresponsive and the user - // decides to touch a window in a different application. - // If the application takes too long to catch up then we drop all events preceding - // the touch into the other window. - MotionEntry* motionEntry = static_cast(entry); - if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN - && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) - && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY - && mInputTargetWaitApplicationToken != nullptr) { - int32_t displayId = motionEntry->displayId; - int32_t x = int32_t(motionEntry->pointerCoords[0]. - getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(motionEntry->pointerCoords[0]. - getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); - if (touchedWindowHandle != nullptr - && touchedWindowHandle->getApplicationToken() - != mInputTargetWaitApplicationToken) { - // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - mNextUnblockedEvent = motionEntry; - needWake = true; - } - } - break; - } - } - - return needWake; -} - -void InputDispatcher::addRecentEventLocked(EventEntry* entry) { - entry->refCount += 1; - mRecentQueue.push_back(entry); - if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { - mRecentQueue.front()->release(); - mRecentQueue.pop_front(); - } -} - -sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, - int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) { - // Traverse windows from front to back to find touched window. - const std::vector> windowHandles = getWindowHandlesLocked(displayId); - for (const sp& windowHandle : windowHandles) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - if (windowInfo->displayId == displayId) { - int32_t flags = windowInfo->layoutParamsFlags; - - if (windowInfo->visible) { - if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE - | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; - if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { - int32_t portalToDisplayId = windowInfo->portalToDisplayId; - if (portalToDisplayId != ADISPLAY_ID_NONE - && portalToDisplayId != displayId) { - if (addPortalWindows) { - // For the monitoring channels of the display. - mTempTouchState.addPortalWindow(windowHandle); - } - return findTouchedWindowAtLocked( - portalToDisplayId, x, y, addOutsideTargets, addPortalWindows); - } - // Found window. - return windowHandle; - } - } - - if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) { - mTempTouchState.addOrUpdateWindow( - windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0)); - } - } - } - } - return nullptr; -} - -std::vector InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector>& portalWindows) { - std::vector touchedMonitors; - - std::vector monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); - addGestureMonitors(monitors, touchedMonitors); - for (const sp& portalWindow : portalWindows) { - const InputWindowInfo* windowInfo = portalWindow->getInfo(); - monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); - addGestureMonitors(monitors, touchedMonitors, - -windowInfo->frameLeft, -windowInfo->frameTop); - } - return touchedMonitors; -} - -void InputDispatcher::addGestureMonitors(const std::vector& monitors, - std::vector& outTouchedMonitors, float xOffset, float yOffset) { - if (monitors.empty()) { - return; - } - outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size()); - for (const Monitor& monitor : monitors) { - outTouchedMonitors.emplace_back(monitor, xOffset, yOffset); - } -} - -void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { - const char* reason; - switch (dropReason) { - case DROP_REASON_POLICY: -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("Dropped event because policy consumed it."); -#endif - reason = "inbound event was dropped because the policy consumed it"; - break; - case DROP_REASON_DISABLED: - if (mLastDropReason != DROP_REASON_DISABLED) { - ALOGI("Dropped event because input dispatch is disabled."); - } - reason = "inbound event was dropped because input dispatch is disabled"; - break; - case DROP_REASON_APP_SWITCH: - ALOGI("Dropped event because of pending overdue app switch."); - reason = "inbound event was dropped because of pending overdue app switch"; - break; - case DROP_REASON_BLOCKED: - ALOGI("Dropped event because the current application is not responding and the user " - "has started interacting with a different application."); - reason = "inbound event was dropped because the current application is not responding " - "and the user has started interacting with a different application"; - break; - case DROP_REASON_STALE: - ALOGI("Dropped event because it is stale."); - reason = "inbound event was dropped because it is stale"; - break; - default: - ALOG_ASSERT(false); - return; - } - - switch (entry->type) { - case EventEntry::TYPE_KEY: { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - break; - } - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(entry); - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - } else { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - } - break; - } - } -} - -static bool isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME - || keyCode == AKEYCODE_ENDCALL - || keyCode == AKEYCODE_APP_SWITCH; -} - -bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) { - return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) - && isAppSwitchKeyCode(keyEntry->keyCode) - && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) - && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); -} - -bool InputDispatcher::isAppSwitchPendingLocked() { - return mAppSwitchDueTime != LONG_LONG_MAX; -} - -void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { - mAppSwitchDueTime = LONG_LONG_MAX; - -#if DEBUG_APP_SWITCH - if (handled) { - ALOGD("App switch has arrived."); - } else { - ALOGD("App switch was abandoned."); - } -#endif -} - -bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { - return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT; -} - -bool InputDispatcher::haveCommandsLocked() const { - return !mCommandQueue.empty(); -} - -bool InputDispatcher::runCommandsLockedInterruptible() { - if (mCommandQueue.empty()) { - return false; - } - - do { - std::unique_ptr commandEntry = std::move(mCommandQueue.front()); - mCommandQueue.pop_front(); - Command command = commandEntry->command; - command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible' - - commandEntry->connection.clear(); - } while (!mCommandQueue.empty()); - return true; -} - -void InputDispatcher::postCommandLocked(std::unique_ptr commandEntry) { - mCommandQueue.push_back(std::move(commandEntry)); -} - -void InputDispatcher::drainInboundQueueLocked() { - while (!mInboundQueue.empty()) { - EventEntry* entry = mInboundQueue.front(); - mInboundQueue.pop_front(); - releaseInboundEventLocked(entry); - } - traceInboundQueueLengthLocked(); -} - -void InputDispatcher::releasePendingEventLocked() { - if (mPendingEvent) { - resetANRTimeoutsLocked(); - releaseInboundEventLocked(mPendingEvent); - mPendingEvent = nullptr; - } -} - -void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("Injected inbound event was dropped."); -#endif - setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED); - } - if (entry == mNextUnblockedEvent) { - mNextUnblockedEvent = nullptr; - } - addRecentEventLocked(entry); - entry->release(); -} - -void InputDispatcher::resetKeyRepeatLocked() { - if (mKeyRepeatState.lastKeyEntry) { - mKeyRepeatState.lastKeyEntry->release(); - mKeyRepeatState.lastKeyEntry = nullptr; - } -} - -InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { - KeyEntry* entry = mKeyRepeatState.lastKeyEntry; - - // Reuse the repeated key entry if it is otherwise unreferenced. - uint32_t policyFlags = entry->policyFlags & - (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); - if (entry->refCount == 1) { - entry->recycle(); - entry->eventTime = currentTime; - entry->policyFlags = policyFlags; - entry->repeatCount += 1; - } else { - KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - entry->deviceId, entry->source, entry->displayId, policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount + 1, entry->downTime); - - mKeyRepeatState.lastKeyEntry = newEntry; - entry->release(); - - entry = newEntry; - } - entry->syntheticRepeat = true; - - // Increment reference count since we keep a reference to the event in - // mKeyRepeatState.lastKeyEntry in addition to the one we return. - entry->refCount += 1; - - mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay; - return entry; -} - -bool InputDispatcher::dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime); -#endif - - // Reset key repeating in case a keyboard device was added or removed or something. - resetKeyRepeatLocked(); - - // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); - commandEntry->eventTime = entry->eventTime; - postCommandLocked(std::move(commandEntry)); - return true; -} - -bool InputDispatcher::dispatchDeviceResetLocked( - nsecs_t currentTime, DeviceResetEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime, - entry->deviceId); -#endif - - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "device was reset"); - options.deviceId = entry->deviceId; - synthesizeCancelationEventsForAllConnectionsLocked(options); - return true; -} - -bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { - // Preprocessing. - if (! entry->dispatchInProgress) { - if (entry->repeatCount == 0 - && entry->action == AKEY_EVENT_ACTION_DOWN - && (entry->policyFlags & POLICY_FLAG_TRUSTED) - && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { - if (mKeyRepeatState.lastKeyEntry - && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { - // We have seen two identical key downs in a row which indicates that the device - // driver is automatically generating key repeats itself. We take note of the - // repeat here, but we disable our own next key repeat timer since it is clear that - // we will not need to synthesize key repeats ourselves. - entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves - } else { - // Not a repeat. Save key down state in case we do see a repeat later. - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout; - } - mKeyRepeatState.lastKeyEntry = entry; - entry->refCount += 1; - } else if (! entry->syntheticRepeat) { - resetKeyRepeatLocked(); - } - - if (entry->repeatCount == 1) { - entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; - } else { - entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; - } - - entry->dispatchInProgress = true; - - logOutboundKeyDetails("dispatchKey - ", entry); - } - - // Handle case where the policy asked us to try again later last time. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { - if (currentTime < entry->interceptKeyWakeupTime) { - if (entry->interceptKeyWakeupTime < *nextWakeupTime) { - *nextWakeupTime = entry->interceptKeyWakeupTime; - } - return false; // wait until next wakeup - } - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; - entry->interceptKeyWakeupTime = 0; - } - - // Give the policy a chance to intercept the key. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { - if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - sp focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); - if (focusedWindowHandle != nullptr) { - commandEntry->inputChannel = - getInputChannelLocked(focusedWindowHandle->getToken()); - } - commandEntry->keyEntry = entry; - postCommandLocked(std::move(commandEntry)); - entry->refCount += 1; - return false; // wait for the command to run - } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; - } - } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { - if (*dropReason == DROP_REASON_NOT_DROPPED) { - *dropReason = DROP_REASON_POLICY; - } - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - setInjectionResult(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - mReporter->reportDroppedKey(entry->sequenceNum); - return true; - } - - // Identify targets. - std::vector inputTargets; - int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime); - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResult(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - // Add monitor channels from event's or focused display. - addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); - - // Dispatch the key. - dispatchEventLocked(currentTime, entry, inputTargets); - return true; -} - -void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " - "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " - "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); -#endif -} - -bool InputDispatcher::dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { - ATRACE_CALL(); - // Preprocessing. - if (! entry->dispatchInProgress) { - entry->dispatchInProgress = true; - - logOutboundMotionDetails("dispatchMotion - ", entry); - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - setInjectionResult(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - return true; - } - - bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; - - // Identify targets. - std::vector inputTargets; - - bool conflictingPointerActions = false; - int32_t injectionResult; - if (isPointerEvent) { - // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime, &conflictingPointerActions); - } else { - // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime); - } - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResult(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) { - CancelationOptions::Mode mode(isPointerEvent ? - CancelationOptions::CANCEL_POINTER_EVENTS : - CancelationOptions::CANCEL_NON_POINTER_EVENTS); - CancelationOptions options(mode, "input event injection failed"); - synthesizeCancelationEventsForMonitorsLocked(options); - } - return true; - } - - // Add monitor channels from event's or focused display. - addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); - - if (isPointerEvent) { - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId); - if (stateIndex >= 0) { - const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex); - if (!state.portalWindows.empty()) { - // The event has gone through these portal windows, so we add monitoring targets of - // the corresponding displays as well. - for (size_t i = 0; i < state.portalWindows.size(); i++) { - const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo(); - addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId, - -windowInfo->frameLeft, -windowInfo->frameTop); - } - } - } - } - - // Dispatch the motion. - if (conflictingPointerActions) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "conflicting pointer actions"); - synthesizeCancelationEventsForAllConnectionsLocked(options); - } - dispatchEventLocked(currentTime, entry, inputTargets); - return true; -} - - -void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, " - "metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags, - entry->action, entry->actionButton, entry->flags, - entry->metaState, entry->buttonState, - entry->edgeFlags, entry->xPrecision, entry->yPrecision, - entry->downTime); - - for (uint32_t i = 0; i < entry->pointerCount; i++) { - ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, entry->pointerProperties[i].id, - entry->pointerProperties[i].toolType, - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - } -#endif -} - -void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, - EventEntry* eventEntry, const std::vector& inputTargets) { - ATRACE_CALL(); -#if DEBUG_DISPATCH_CYCLE - ALOGD("dispatchEventToCurrentInputTargets"); -#endif - - ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true - - pokeUserActivityLocked(eventEntry); - - for (const InputTarget& inputTarget : inputTargets) { - sp connection = getConnectionLocked(inputTarget.inputChannel); - if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); - } else { -#if DEBUG_FOCUS - ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); -#endif - } - } -} - -int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, - const EventEntry* entry, - const sp& applicationHandle, - const sp& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) { - if (applicationHandle == nullptr && windowHandle == nullptr) { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); -#endif - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = LONG_LONG_MAX; - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - } - } else { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), - reason); -#endif - nsecs_t timeout; - if (windowHandle != nullptr) { - timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else if (applicationHandle != nullptr) { - timeout = applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else { - timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; - } - - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - - if (windowHandle != nullptr) { - mInputTargetWaitApplicationToken = windowHandle->getApplicationToken(); - } - if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) { - mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken(); - } - } - } - - if (mInputTargetWaitTimeoutExpired) { - return INPUT_EVENT_INJECTION_TIMED_OUT; - } - - if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, applicationHandle, windowHandle, - entry->eventTime, mInputTargetWaitStartTime, reason); - - // Force poll loop to wake up immediately on next iteration once we get the - // ANR response back from the policy. - *nextWakeupTime = LONG_LONG_MIN; - return INPUT_EVENT_INJECTION_PENDING; - } else { - // Force poll loop to wake up when timeout is due. - if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = mInputTargetWaitTimeoutTime; - } - return INPUT_EVENT_INJECTION_PENDING; - } -} - -void InputDispatcher::removeWindowByTokenLocked(const sp& token) { - for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { - TouchState& state = mTouchStatesByDisplay.editValueAt(d); - state.removeWindowByToken(token); - } -} - -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputChannel) { - if (newTimeout > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Input state will not be realistic. Mark it out of sync. - sp connection = getConnectionLocked(inputChannel); - if (connection != nullptr) { - sp token = connection->inputChannel->getToken(); - - if (token != nullptr) { - removeWindowByTokenLocked(token); - } - - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } - } - } -} - -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( - nsecs_t currentTime) { - if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - return currentTime - mInputTargetWaitStartTime; - } - return 0; -} - -void InputDispatcher::resetANRTimeoutsLocked() { -#if DEBUG_FOCUS - ALOGD("Resetting ANR timeouts."); -#endif - - // Reset input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplicationToken.clear(); -} - -/** - * Get the display id that the given event should go to. If this event specifies a valid display id, - * then it should be dispatched to that display. Otherwise, the event goes to the focused display. - * Focused display is the display that the user most recently interacted with. - */ -int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { - int32_t displayId; - switch (entry->type) { - case EventEntry::TYPE_KEY: { - const KeyEntry* typedEntry = static_cast(entry); - displayId = typedEntry->displayId; - break; - } - case EventEntry::TYPE_MOTION: { - const MotionEntry* typedEntry = static_cast(entry); - displayId = typedEntry->displayId; - break; - } - default: { - ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); - return ADISPLAY_ID_NONE; - } - } - return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; -} - -int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, std::vector& inputTargets, nsecs_t* nextWakeupTime) { - int32_t injectionResult; - std::string reason; - - int32_t displayId = getTargetDisplayId(entry); - sp focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - sp focusedApplicationHandle = - getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); - - // If there is no currently focused window and no focused application - // then drop the event. - if (focusedWindowHandle == nullptr) { - if (focusedApplicationHandle != nullptr) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - focusedApplicationHandle, nullptr, nextWakeupTime, - "Waiting because no window has focus but there is a " - "focused application that may eventually add a window " - "when it finishes starting up."); - goto Unresponsive; - } - - ALOGI("Dropping event because there is no focused window or focused application in display " - "%" PRId32 ".", displayId); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Check permissions. - if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - goto Failed; - } - - // Check whether the window is ready for more input. - reason = checkWindowReadyForMoreInputLocked(currentTime, - focusedWindowHandle, entry, "focused"); - if (!reason.empty()) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str()); - goto Unresponsive; - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(focusedWindowHandle, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), - inputTargets); - - // Done. -Failed: -Unresponsive: - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, std::vector& inputTargets, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) { - ATRACE_CALL(); - enum InjectionPermission { - INJECTION_PERMISSION_UNKNOWN, - INJECTION_PERMISSION_GRANTED, - INJECTION_PERMISSION_DENIED - }; - - // For security reasons, we defer updating the touch state until we are sure that - // event injection will be allowed. - int32_t displayId = entry->displayId; - int32_t action = entry->action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - - // Update the touch state as needed based on the properties of the touch event. - int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; - InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - sp newHoverWindowHandle; - - // Copy current touch state into mTempTouchState. - // This state is always reset at the end of this function, so if we don't find state - // for the specified display then our initial state will be empty. - const TouchState* oldState = nullptr; - ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (oldStateIndex >= 0) { - oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex); - mTempTouchState.copyFrom(*oldState); - } - - bool isSplit = mTempTouchState.split; - bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 - && (mTempTouchState.deviceId != entry->deviceId - || mTempTouchState.source != entry->source - || mTempTouchState.displayId != displayId); - bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER - || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); - bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN - || maskedAction == AMOTION_EVENT_ACTION_SCROLL - || isHoverAction); - const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; - bool wrongDevice = false; - if (newGesture) { - bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { -#if DEBUG_FOCUS - ALOGD("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, displayId); -#endif - // TODO: test multiple simultaneous input streams. - injectionResult = INPUT_EVENT_INJECTION_FAILED; - switchedDevice = false; - wrongDevice = true; - goto Failed; - } - mTempTouchState.reset(); - mTempTouchState.down = down; - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; - mTempTouchState.displayId = displayId; - isSplit = false; - } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { -#if DEBUG_FOCUS - ALOGI("Dropping move event because a pointer for a different device is already active " - "in display %" PRId32, displayId); -#endif - // TODO: test multiple simultaneous input streams. - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - switchedDevice = false; - wrongDevice = true; - goto Failed; - } - - if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { - /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ - - int32_t x; - int32_t y; - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - // Always dispatch mouse events to cursor position. - if (isFromMouse) { - x = int32_t(entry->xCursorPosition); - y = int32_t(entry->yCursorPosition); - } else { - x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); - y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); - } - bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; - sp newTouchedWindowHandle = findTouchedWindowAtLocked( - displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); - - std::vector newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows) - : std::vector{}; - - // Figure out whether splitting will be allowed for this window. - if (newTouchedWindowHandle != nullptr - && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - // New window supports splitting, but we should never split mouse events. - isSplit = !isFromMouse; - } else if (isSplit) { - // New window does not support splitting but we have already split events. - // Ignore the new window. - newTouchedWindowHandle = nullptr; - } - - // Handle the case where we did not find a window. - if (newTouchedWindowHandle == nullptr) { - // Try to assign the pointer to the first foreground window we find, if there is one. - newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); - } - - if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { - ALOGI("Dropping event because there is no touchable window or gesture monitor at " - "(%d, %d) in display %" PRId32 ".", x, y, displayId); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - if (newTouchedWindowHandle != nullptr) { - // Set target flags. - int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS; - if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; - } - if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - } - - // Update hover state. - if (isHoverAction) { - newHoverWindowHandle = newTouchedWindowHandle; - } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { - newHoverWindowHandle = mLastHoverWindowHandle; - } - - // Update the temporary touch state. - BitSet32 pointerIds; - if (isSplit) { - uint32_t pointerId = entry->pointerProperties[pointerIndex].id; - pointerIds.markBit(pointerId); - } - mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); - } - - mTempTouchState.addGestureMonitors(newGestureMonitors); - } else { - /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ - - // If the pointer is not currently down, then ignore the event. - if (! mTempTouchState.down) { -#if DEBUG_FOCUS - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, displayId); -#endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Check whether touches should slip outside of the current foreground window. - if (maskedAction == AMOTION_EVENT_ACTION_MOVE - && entry->pointerCount == 1 - && mTempTouchState.isSlippery()) { - int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - - sp oldTouchedWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); - sp newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y); - if (oldTouchedWindowHandle != newTouchedWindowHandle - && oldTouchedWindowHandle != nullptr - && newTouchedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, - oldTouchedWindowHandle->getName().c_str(), - newTouchedWindowHandle->getName().c_str(), - displayId); -#endif - // Make a slippery exit from the old window. - mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); - - // Make a slippery entrance into the new window. - if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - isSplit = true; - } - - int32_t targetFlags = InputTarget::FLAG_FOREGROUND - | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; - if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; - } - if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } - - BitSet32 pointerIds; - if (isSplit) { - pointerIds.markBit(entry->pointerProperties[0].id); - } - mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); - } - } - } - - if (newHoverWindowHandle != mLastHoverWindowHandle) { - // Let the previous window know that the hover sequence is over. - if (mLastHoverWindowHandle != nullptr) { -#if DEBUG_HOVER - ALOGD("Sending hover exit event to window %s.", - mLastHoverWindowHandle->getName().c_str()); -#endif - mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); - } - - // Let the new window know that the hover sequence is starting. - if (newHoverWindowHandle != nullptr) { -#if DEBUG_HOVER - ALOGD("Sending hover enter event to window %s.", - newHoverWindowHandle->getName().c_str()); -#endif - mTempTouchState.addOrUpdateWindow(newHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); - } - } - - // Check permission to inject into all touched foreground windows and ensure there - // is at least one touched foreground window. - { - bool haveForegroundWindow = false; - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - haveForegroundWindow = true; - if (! checkInjectionPermission(touchedWindow.windowHandle, - entry->injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - injectionPermission = INJECTION_PERMISSION_DENIED; - goto Failed; - } - } - } - bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); - if (!haveForegroundWindow && !hasGestureMonitor) { -#if DEBUG_FOCUS - ALOGD("Dropping event because there is no touched foreground window in display %" - PRId32 " or gesture monitor to receive it.", displayId); -#endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Permission granted to injection into all touched foreground windows. - injectionPermission = INJECTION_PERMISSION_GRANTED; - } - - // Check whether windows listening for outside touches are owned by the same UID. If it is - // set the policy flag that we will not reveal coordinate information to this window. - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - sp foregroundWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); - if (foregroundWindowHandle) { - const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - sp inputWindowHandle = touchedWindow.windowHandle; - if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) { - mTempTouchState.addOrUpdateWindow(inputWindowHandle, - InputTarget::FLAG_ZERO_COORDS, BitSet32(0)); - } - } - } - } - } - - // Ensure all touched foreground windows are ready for new input. - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // Check whether the window is ready for more input. - std::string reason = checkWindowReadyForMoreInputLocked(currentTime, - touchedWindow.windowHandle, entry, "touched"); - if (!reason.empty()) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); - goto Unresponsive; - } - } - } - - // If this is the first pointer going down and the touched window has a wallpaper - // then also add the touched wallpaper windows so they are locked in for the duration - // of the touch gesture. - // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper - // engine only supports touch events. We would need to add a mechanism similar - // to View.onGenericMotionEvent to enable wallpapers to handle these events. - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - sp foregroundWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); - if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) { - const std::vector> windowHandles = - getWindowHandlesLocked(displayId); - for (const sp& windowHandle : windowHandles) { - const InputWindowInfo* info = windowHandle->getInfo(); - if (info->displayId == displayId - && windowHandle->getInfo()->layoutParamsType - == InputWindowInfo::TYPE_WALLPAPER) { - mTempTouchState.addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED - | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED - | InputTarget::FLAG_DISPATCH_AS_IS, - BitSet32(0)); - } - } - } - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { - addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, - touchedWindow.pointerIds, inputTargets); - } - - for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) { - addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, - touchedMonitor.yOffset, inputTargets); - } - - // Drop the outside or hover touch windows since we will not care about them - // in the next iteration. - mTempTouchState.filterNonAsIsTouchWindows(); - -Failed: - // Check injection permission once and for all. - if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { - if (checkInjectionPermission(nullptr, entry->injectionState)) { - injectionPermission = INJECTION_PERMISSION_GRANTED; - } else { - injectionPermission = INJECTION_PERMISSION_DENIED; - } - } - - // Update final pieces of touch state if the injector had permission. - if (injectionPermission == INJECTION_PERMISSION_GRANTED) { - if (!wrongDevice) { - if (switchedDevice) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Switched to a different device."); -#endif - *outConflictingPointerActions = true; - } - - if (isHoverAction) { - // Started hovering, therefore no longer down. - if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Hover received while pointer was down."); -#endif - *outConflictingPointerActions = true; - } - mTempTouchState.reset(); - if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER - || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; - mTempTouchState.displayId = displayId; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - mTempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Down received while already down."); -#endif - *outConflictingPointerActions = true; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - if (isSplit) { - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry->pointerProperties[pointerIndex].id; - - for (size_t i = 0; i < mTempTouchState.windows.size(); ) { - TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i); - continue; - } - } - i += 1; - } - } - } - - // Save changes unless the action was scroll in which case the temporary touch - // state was only valid for this one action. - if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (mTempTouchState.displayId >= 0) { - if (oldStateIndex >= 0) { - mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState); - } else { - mTouchStatesByDisplay.add(displayId, mTempTouchState); - } - } else if (oldStateIndex >= 0) { - mTouchStatesByDisplay.removeItemsAt(oldStateIndex); - } - } - - // Update hover state. - mLastHoverWindowHandle = newHoverWindowHandle; - } - } else { -#if DEBUG_FOCUS - ALOGD("Not updating touch focus because injection was denied."); -#endif - } - -Unresponsive: - // Reset temporary touch state to ensure we release unnecessary references to input channels. - mTempTouchState.reset(); - - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } - - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget target; - target.inputChannel = inputChannel; - target.flags = targetFlags; - target.xOffset = - windowInfo->frameLeft; - target.yOffset = - windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - target.pointerIds = pointerIds; - inputTargets.push_back(target); -} - -void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, - int32_t displayId, float xOffset, float yOffset) { - - std::unordered_map>::const_iterator it = - mGlobalMonitorsByDisplay.find(displayId); - - if (it != mGlobalMonitorsByDisplay.end()) { - const std::vector& monitors = it->second; - for (const Monitor& monitor : monitors) { - addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets); - } - } -} - -void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, - float xOffset, float yOffset, std::vector& inputTargets) { - InputTarget target; - target.inputChannel = monitor.inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = xOffset; - target.yOffset = yOffset; - target.pointerIds.clear(); - target.globalScaleFactor = 1.0f; - inputTargets.push_back(target); -} - -bool InputDispatcher::checkInjectionPermission(const sp& windowHandle, - const InjectionState* injectionState) { - if (injectionState - && (windowHandle == nullptr - || windowHandle->getInfo()->ownerUid != injectionState->injectorUid) - && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (windowHandle != nullptr) { - ALOGW("Permission denied: injecting event from pid %d uid %d to window %s " - "owned by uid %d", - injectionState->injectorPid, injectionState->injectorUid, - windowHandle->getName().c_str(), - windowHandle->getInfo()->ownerUid); - } else { - ALOGW("Permission denied: injecting event from pid %d uid %d", - injectionState->injectorPid, injectionState->injectorUid); - } - return false; - } - return true; -} - -bool InputDispatcher::isWindowObscuredAtPointLocked( - const sp& windowHandle, int32_t x, int32_t y) const { - int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector> windowHandles = getWindowHandlesLocked(displayId); - for (const sp& otherHandle : windowHandles) { - if (otherHandle == windowHandle) { - break; - } - - const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId - && otherInfo->visible && !otherInfo->isTrustedOverlay() - && otherInfo->frameContainsPoint(x, y)) { - return true; - } - } - return false; -} - - -bool InputDispatcher::isWindowObscuredLocked(const sp& windowHandle) const { - int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector> windowHandles = getWindowHandlesLocked(displayId); - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - for (const sp& otherHandle : windowHandles) { - if (otherHandle == windowHandle) { - break; - } - - const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId - && otherInfo->visible && !otherInfo->isTrustedOverlay() - && otherInfo->overlaps(windowInfo)) { - return true; - } - } - return false; -} - -std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp& windowHandle, const EventEntry* eventEntry, - const char* targetType) { - // If the window is paused then keep waiting. - if (windowHandle->getInfo()->paused) { - return StringPrintf("Waiting because the %s window is paused.", targetType); - } - - // If the window's connection is not registered then keep waiting. - sp connection = - getConnectionLocked(getInputChannelLocked(windowHandle->getToken())); - if (connection == nullptr) { - return StringPrintf("Waiting because the %s window's input channel is not " - "registered with the input dispatcher. The window may be in the process " - "of being removed.", targetType); - } - - // If the connection is dead then keep waiting. - if (connection->status != Connection::STATUS_NORMAL) { - return StringPrintf("Waiting because the %s window's input connection is %s." - "The window may be in the process of being removed.", targetType, - connection->getStatusLabel()); - } - - // If the connection is backed up then keep waiting. - if (connection->inputPublisherBlocked) { - return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %zu. Wait queue length: %zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - - // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry->type == EventEntry::TYPE_KEY) { - // If the event is a key event, then we must wait for all previous events to - // complete before delivering it because previous events may have the - // side-effect of transferring focus to a different window and we want to - // ensure that the following keys are sent to the new window. - // - // Suppose the user touches a button in a window then immediately presses "A". - // If the button causes a pop-up window to appear then we want to ensure that - // the "A" key is delivered to the new pop-up window. This is because users - // often anticipate pending UI changes when typing on a keyboard. - // To obtain this behavior, we must serialize key events with respect to all - // prior input events. - if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { - return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %zu. Wait queue length: " - "%zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - } else { - // Touch events can always be sent to a window immediately because the user intended - // to touch whatever was visible at the time. Even if focus changes or a new - // window appears moments later, the touch event was meant to be delivered to - // whatever window happened to be on screen at the time. - // - // Generic motion events, such as trackball or joystick events are a little trickier. - // Like key events, generic motion events are delivered to the focused window. - // Unlike key events, generic motion events don't tend to transfer focus to other - // windows and it is not important for them to be serialized. So we prefer to deliver - // generic motion events as soon as possible to improve efficiency and reduce lag - // through batching. - // - // The one case where we pause input event delivery is when the wait queue is piling - // up with lots of events because the application is not responding. - // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.empty() && - currentTime >= - connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { - return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to " - "it over " - "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " - "%0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.size(), - (currentTime - connection->waitQueue.front()->deliveryTime) * - 0.000001f); - } - } - return ""; -} - -std::string InputDispatcher::getApplicationWindowLabel( - const sp& applicationHandle, - const sp& windowHandle) { - if (applicationHandle != nullptr) { - if (windowHandle != nullptr) { - std::string label(applicationHandle->getName()); - label += " - "; - label += windowHandle->getName(); - return label; - } else { - return applicationHandle->getName(); - } - } else if (windowHandle != nullptr) { - return windowHandle->getName(); - } else { - return ""; - } -} - -void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - int32_t displayId = getTargetDisplayId(eventEntry); - sp focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - if (focusedWindowHandle != nullptr) { - const InputWindowInfo* info = focusedWindowHandle->getInfo(); - if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); -#endif - return; - } - } - - int32_t eventType = USER_ACTIVITY_EVENT_OTHER; - switch (eventEntry->type) { - case EventEntry::TYPE_MOTION: { - const MotionEntry* motionEntry = static_cast(eventEntry); - if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { - return; - } - - if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { - eventType = USER_ACTIVITY_EVENT_TOUCH; - } - break; - } - case EventEntry::TYPE_KEY: { - const KeyEntry* keyEntry = static_cast(eventEntry); - if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { - return; - } - eventType = USER_ACTIVITY_EVENT_BUTTON; - break; - } - } - - std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doPokeUserActivityLockedInterruptible); - commandEntry->eventTime = eventEntry->eventTime; - commandEntry->userActivityEventType = eventType; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, - const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { - if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); - ATRACE_NAME(message.c_str()); - } -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " - "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget->flags, - inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, - inputTarget->windowXScale, inputTarget->windowYScale, - inputTarget->pointerIds.value); -#endif - - // Skip this event if the connection status is not normal. - // We don't want to enqueue additional outbound events if the connection is broken. - if (connection->status != Connection::STATUS_NORMAL) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName().c_str(), connection->getStatusLabel()); -#endif - return; - } - - // Split a motion event if needed. - if (inputTarget->flags & InputTarget::FLAG_SPLIT) { - ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); - - MotionEntry* originalMotionEntry = static_cast(eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { - MotionEntry* splitMotionEntry = splitMotionEvent( - originalMotionEntry, inputTarget->pointerIds); - if (!splitMotionEntry) { - return; // split event was dropped - } -#if DEBUG_FOCUS - ALOGD("channel '%s' ~ Split motion event.", - connection->getInputChannelName().c_str()); - logOutboundMotionDetails(" ", splitMotionEntry); -#endif - enqueueDispatchEntriesLocked(currentTime, connection, - splitMotionEntry, inputTarget); - splitMotionEntry->release(); - return; - } - } - - // Not splitting. Enqueue dispatch entries for the event as is. - enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); -} - -void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, - const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { - if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); - ATRACE_NAME(message.c_str()); - } - - bool wasEmpty = connection->outboundQueue.empty(); - - // Enqueue dispatch entries for the requested modes. - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_OUTSIDE); - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_IS); - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); - enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); - - // If the outbound queue was previously empty, start the dispatch cycle going. - if (wasEmpty && !connection->outboundQueue.empty()) { - startDispatchCycleLocked(currentTime, connection); - } -} - -void InputDispatcher::enqueueDispatchEntryLocked( - const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget, - int32_t dispatchMode) { - if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", - connection->getInputChannelName().c_str(), - dispatchModeToString(dispatchMode).c_str()); - ATRACE_NAME(message.c_str()); - } - int32_t inputTargetFlags = inputTarget->flags; - if (!(inputTargetFlags & dispatchMode)) { - return; - } - inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; - - // This is a new event. - // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref - inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale); - - // Apply target flags and update the connection's input state. - switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast(eventEntry); - dispatchEntry->resolvedAction = keyEntry->action; - dispatchEntry->resolvedFlags = keyEntry->flags; - - if (!connection->inputState.trackKey(keyEntry, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", - connection->getInputChannelName().c_str()); -#endif - delete dispatchEntry; - return; // skip the inconsistent event - } - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(eventEntry); - if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; - } else { - dispatchEntry->resolvedAction = motionEntry->action; - } - if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - && !connection->inputState.isHovering( - motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event", - connection->getInputChannelName().c_str()); -#endif - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } - - dispatchEntry->resolvedFlags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { - dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { - dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - } - - if (!connection->inputState.trackMotion(motionEntry, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event", - connection->getInputChannelName().c_str()); -#endif - delete dispatchEntry; - return; // skip the inconsistent event - } - - dispatchPointerDownOutsideFocus(motionEntry->source, - dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken()); - - break; - } - } - - // Remember that we are waiting for this dispatch to complete. - if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatches(eventEntry); - } - - // Enqueue the dispatch entry. - connection->outboundQueue.push_back(dispatchEntry); - traceOutboundQueueLength(connection); - -} - -void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, - const sp& newToken) { - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK; - if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) { - return; - } - - sp inputWindowHandle = getWindowHandleLocked(newToken); - if (inputWindowHandle == nullptr) { - return; - } - - sp focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); - - bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken; - - if (!hasFocusChanged) { - return; - } - - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); - commandEntry->newToken = newToken; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp& connection) { - if (ATRACE_ENABLED()) { - std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)", - connection->getInputChannelName().c_str()); - ATRACE_NAME(message.c_str()); - } -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ startDispatchCycle", - connection->getInputChannelName().c_str()); -#endif - - while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.front(); - dispatchEntry->deliveryTime = currentTime; - - // Publish the event. - status_t status; - EventEntry* eventEntry = dispatchEntry->eventEntry; - switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast(eventEntry); - - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, - keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, - keyEntry->keyCode, keyEntry->scanCode, - keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(eventEntry); - - PointerCoords scaledCoords[MAX_POINTERS]; - const PointerCoords* usingCoords = motionEntry->pointerCoords; - - // Set the X and Y offset depending on the input source. - float xOffset, yOffset; - if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) - && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { - float globalScaleFactor = dispatchEntry->globalScaleFactor; - float wxs = dispatchEntry->windowXScale; - float wys = dispatchEntry->windowYScale; - xOffset = dispatchEntry->xOffset * wxs; - yOffset = dispatchEntry->yOffset * wys; - if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = motionEntry->pointerCoords[i]; - scaledCoords[i].scale(globalScaleFactor, wxs, wys); - } - usingCoords = scaledCoords; - } - } else { - xOffset = 0.0f; - yOffset = 0.0f; - - // We don't want the dispatch target to know. - if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i].clear(); - } - usingCoords = scaledCoords; - } - } - - // Publish the motion event. - status = - connection->inputPublisher - .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, - motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, - motionEntry->actionButton, - dispatchEntry->resolvedFlags, - motionEntry->edgeFlags, motionEntry->metaState, - motionEntry->buttonState, - motionEntry->classification, xOffset, yOffset, - motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->xCursorPosition, - motionEntry->yCursorPosition, motionEntry->downTime, - motionEntry->eventTime, motionEntry->pointerCount, - motionEntry->pointerProperties, usingCoords); - break; - } - - default: - ALOG_ASSERT(false); - return; - } - - // Check the result. - if (status) { - if (status == WOULD_BLOCK) { - if (connection->waitQueue.empty()) { - ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " - "This is unexpected because the wait queue is empty, so the pipe " - "should be empty and we shouldn't have any problems writing an " - "event to it, status=%d", connection->getInputChannelName().c_str(), - status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - } else { - // Pipe is full and we are waiting for the app to finish process some events - // before sending more events to it. -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " - "waiting for the application to catch up", - connection->getInputChannelName().c_str()); -#endif - connection->inputPublisherBlocked = true; - } - } else { - ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " - "status=%d", connection->getInputChannelName().c_str(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - } - return; - } - - // Re-enqueue the event on the wait queue. - connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), - connection->outboundQueue.end(), - dispatchEntry)); - traceOutboundQueueLength(connection); - connection->waitQueue.push_back(dispatchEntry); - traceWaitQueueLength(connection); - } -} - -void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, - const sp& connection, uint32_t seq, bool handled) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", - connection->getInputChannelName().c_str(), seq, toString(handled)); -#endif - - connection->inputPublisherBlocked = false; - - if (connection->status == Connection::STATUS_BROKEN - || connection->status == Connection::STATUS_ZOMBIE) { - return; - } - - // Notify other system components and prepare to start the next dispatch cycle. - onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); -} - -void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const sp& connection, bool notify) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s", - connection->getInputChannelName().c_str(), toString(notify)); -#endif - - // Clear the dispatch queues. - drainDispatchQueue(connection->outboundQueue); - traceOutboundQueueLength(connection); - drainDispatchQueue(connection->waitQueue); - traceWaitQueueLength(connection); - - // The connection appears to be unrecoverably broken. - // Ignore already broken or zombie connections. - if (connection->status == Connection::STATUS_NORMAL) { - connection->status = Connection::STATUS_BROKEN; - - if (notify) { - // Notify other system components. - onDispatchCycleBrokenLocked(currentTime, connection); - } - } -} - -void InputDispatcher::drainDispatchQueue(std::deque& queue) { - while (!queue.empty()) { - DispatchEntry* dispatchEntry = queue.front(); - queue.pop_front(); - releaseDispatchEntry(dispatchEntry); - } -} - -void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) { - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatches(dispatchEntry->eventEntry); - } - delete dispatchEntry; -} - -int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { - InputDispatcher* d = static_cast(data); - - { // acquire lock - std::scoped_lock _l(d->mLock); - - if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) { - ALOGE("Received spurious receive callback for unknown input channel. " - "fd=%d, events=0x%x", fd, events); - return 0; // remove the callback - } - - bool notify; - sp connection = d->mConnectionsByFd[fd]; - if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { - if (!(events & ALOOPER_EVENT_INPUT)) { - ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", connection->getInputChannelName().c_str(), events); - return 1; - } - - nsecs_t currentTime = now(); - bool gotOne = false; - status_t status; - for (;;) { - uint32_t seq; - bool handled; - status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); - if (status) { - break; - } - d->finishDispatchCycleLocked(currentTime, connection, seq, handled); - gotOne = true; - } - if (gotOne) { - d->runCommandsLockedInterruptible(); - if (status == WOULD_BLOCK) { - return 1; - } - } - - notify = status != DEAD_OBJECT || !connection->monitor; - if (notify) { - ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", - connection->getInputChannelName().c_str(), status); - } - } else { - // Monitor channels are never explicitly unregistered. - // We do it automatically when the remote endpoint is closed so don't warn - // about them. - notify = !connection->monitor; - if (notify) { - ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", connection->getInputChannelName().c_str(), events); - } - } - - // Unregister the channel. - d->unregisterInputChannelLocked(connection->inputChannel, notify); - return 0; // remove the callback - } // release lock -} - -void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked ( - const CancelationOptions& options) { - for (const auto& pair : mConnectionsByFd) { - synthesizeCancelationEventsForConnectionLocked(pair.second, options); - } -} - -void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked ( - const CancelationOptions& options) { - synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay); - synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay); -} - -void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( - const CancelationOptions& options, - std::unordered_map>& monitorsByDisplay) { - for (const auto& it : monitorsByDisplay) { - const std::vector& monitors = it.second; - for (const Monitor& monitor : monitors) { - synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); - } - } -} - -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const sp& channel, const CancelationOptions& options) { - sp connection = getConnectionLocked(channel); - if (connection == nullptr) { - return; - } - - synthesizeCancelationEventsForConnectionLocked(connection, options); -} - -void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( - const sp& connection, const CancelationOptions& options) { - if (connection->status == Connection::STATUS_BROKEN) { - return; - } - - nsecs_t currentTime = now(); - - std::vector cancelationEvents; - connection->inputState.synthesizeCancelationEvents(currentTime, - cancelationEvents, options); - - if (!cancelationEvents.empty()) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " - "with reality: %s, mode=%d.", - connection->getInputChannelName().c_str(), cancelationEvents.size(), - options.reason, options.mode); -#endif - for (size_t i = 0; i < cancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = cancelationEvents[i]; - switch (cancelationEventEntry->type) { - case EventEntry::TYPE_KEY: - logOutboundKeyDetails("cancel - ", - static_cast(cancelationEventEntry)); - break; - case EventEntry::TYPE_MOTION: - logOutboundMotionDetails("cancel - ", - static_cast(cancelationEventEntry)); - break; - } - - InputTarget target; - sp windowHandle = getWindowHandleLocked( - connection->inputChannel->getToken()); - if (windowHandle != nullptr) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - } else { - target.xOffset = 0; - target.yOffset = 0; - target.globalScaleFactor = 1.0f; - } - target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - - enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - &target, InputTarget::FLAG_DISPATCH_AS_IS); - - cancelationEventEntry->release(); - } - - startDispatchCycleLocked(currentTime, connection); - } -} - -InputDispatcher::MotionEntry* -InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { - ALOG_ASSERT(pointerIds.value != 0); - - uint32_t splitPointerIndexMap[MAX_POINTERS]; - PointerProperties splitPointerProperties[MAX_POINTERS]; - PointerCoords splitPointerCoords[MAX_POINTERS]; - - uint32_t originalPointerCount = originalMotionEntry->pointerCount; - uint32_t splitPointerCount = 0; - - for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; - originalPointerIndex++) { - const PointerProperties& pointerProperties = - originalMotionEntry->pointerProperties[originalPointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - if (pointerIds.hasBit(pointerId)) { - splitPointerIndexMap[splitPointerCount] = originalPointerIndex; - splitPointerProperties[splitPointerCount].copyFrom(pointerProperties); - splitPointerCoords[splitPointerCount].copyFrom( - originalMotionEntry->pointerCoords[originalPointerIndex]); - splitPointerCount += 1; - } - } - - if (splitPointerCount != pointerIds.count()) { - // This is bad. We are missing some of the pointers that we expected to deliver. - // Most likely this indicates that we received an ACTION_MOVE events that has - // different pointer ids than we expected based on the previous ACTION_DOWN - // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers - // in this way. - ALOGW("Dropping split motion event because the pointer count is %d but " - "we expected there to be %d pointers. This probably means we received " - "a broken sequence of pointer ids from the input device.", - splitPointerCount, pointerIds.count()); - return nullptr; - } - - int32_t action = originalMotionEntry->action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); - const PointerProperties& pointerProperties = - originalMotionEntry->pointerProperties[originalPointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - if (pointerIds.hasBit(pointerId)) { - if (pointerIds.count() == 1) { - // The first/last pointer went down/up. - action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - // A secondary pointer went down/up. - uint32_t splitPointerIndex = 0; - while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) { - splitPointerIndex += 1; - } - action = maskedAction | (splitPointerIndex - << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - } - } else { - // An unrelated pointer changed. - action = AMOTION_EVENT_ACTION_MOVE; - } - } - - MotionEntry* splitMotionEntry = - new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, - originalMotionEntry->deviceId, originalMotionEntry->source, - originalMotionEntry->displayId, originalMotionEntry->policyFlags, - action, originalMotionEntry->actionButton, originalMotionEntry->flags, - originalMotionEntry->metaState, originalMotionEntry->buttonState, - originalMotionEntry->classification, originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, - originalMotionEntry->xCursorPosition, - originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, - splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); - - if (originalMotionEntry->injectionState) { - splitMotionEntry->injectionState = originalMotionEntry->injectionState; - splitMotionEntry->injectionState->refCount += 1; - } - - return splitMotionEntry; -} - -void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime); -#endif - - bool needWake; - { // acquire lock - std::scoped_lock _l(mLock); - - ConfigurationChangedEntry* newEntry = - new ConfigurationChangedEntry(args->sequenceNum, args->eventTime); - needWake = enqueueInboundEventLocked(newEntry); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -/** - * If one of the meta shortcuts is detected, process them here: - * Meta + Backspace -> generate BACK - * Meta + Enter -> generate HOME - * This will potentially overwrite keyCode and metaState. - */ -void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, - int32_t& keyCode, int32_t& metaState) { - if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) { - int32_t newKeyCode = AKEYCODE_UNKNOWN; - if (keyCode == AKEYCODE_DEL) { - newKeyCode = AKEYCODE_BACK; - } else if (keyCode == AKEYCODE_ENTER) { - newKeyCode = AKEYCODE_HOME; - } - if (newKeyCode != AKEYCODE_UNKNOWN) { - std::scoped_lock _l(mLock); - struct KeyReplacement replacement = {keyCode, deviceId}; - mReplacedKeys.add(replacement, newKeyCode); - keyCode = newKeyCode; - metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } - } else if (action == AKEY_EVENT_ACTION_UP) { - // In order to maintain a consistent stream of up and down events, check to see if the key - // going up is one we've replaced in a down event and haven't yet replaced in an up event, - // even if the modifier was released between the down and the up events. - std::scoped_lock _l(mLock); - struct KeyReplacement replacement = {keyCode, deviceId}; - ssize_t index = mReplacedKeys.indexOfKey(replacement); - if (index >= 0) { - keyCode = mReplacedKeys.valueAt(index); - mReplacedKeys.removeItemsAt(index); - metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } - } -} - -void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyKey - eventTime=%" PRId64 - ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->flags, args->keyCode, args->scanCode, - args->metaState, args->downTime); -#endif - if (!validateKeyEvent(args->action)) { - return; - } - - uint32_t policyFlags = args->policyFlags; - int32_t flags = args->flags; - int32_t metaState = args->metaState; - // InputDispatcher tracks and generates key repeats on behalf of - // whatever notifies it, so repeatCount should always be set to 0 - constexpr int32_t repeatCount = 0; - if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { - policyFlags |= POLICY_FLAG_VIRTUAL; - flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; - } - if (policyFlags & POLICY_FLAG_FUNCTION) { - metaState |= AMETA_FUNCTION_ON; - } - - policyFlags |= POLICY_FLAG_TRUSTED; - - int32_t keyCode = args->keyCode; - accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); - - KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - flags, keyCode, args->scanCode, metaState, repeatCount, - args->downTime, args->eventTime); - - android::base::Timer t; - mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - - bool needWake; - { // acquire lock - mLock.lock(); - - if (shouldSendKeyToInputFilterLocked(args)) { - mLock.unlock(); - - policyFlags |= POLICY_FLAG_FILTERED; - if (!mPolicy->filterInputEvent(&event, policyFlags)) { - return; // event was consumed by the filter - } - - mLock.lock(); - } - - KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime, - args->deviceId, args->source, args->displayId, policyFlags, - args->action, flags, keyCode, args->scanCode, - metaState, repeatCount, args->downTime); - - needWake = enqueueInboundEventLocked(newEntry); - mLock.unlock(); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) { - return mInputFilterEnabled; -} - -void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "yCursorPosition=%f, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, - args->yCursorPosition, args->downTime); - for (uint32_t i = 0; i < args->pointerCount; i++) { - ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, args->pointerProperties[i].id, - args->pointerProperties[i].toolType, - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - } -#endif - if (!validateMotionEvent(args->action, args->actionButton, - args->pointerCount, args->pointerProperties)) { - return; - } - - uint32_t policyFlags = args->policyFlags; - policyFlags |= POLICY_FLAG_TRUSTED; - - android::base::Timer t; - mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - - bool needWake; - { // acquire lock - mLock.lock(); - - if (shouldSendMotionToInputFilterLocked(args)) { - mLock.unlock(); - - MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - args->actionButton, args->flags, args->edgeFlags, args->metaState, - args->buttonState, args->classification, 0, 0, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->eventTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); - - policyFlags |= POLICY_FLAG_FILTERED; - if (!mPolicy->filterInputEvent(&event, policyFlags)) { - return; // event was consumed by the filter - } - - mLock.lock(); - } - - // Just enqueue a new motion event. - MotionEntry* newEntry = - new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, - args->displayId, policyFlags, args->action, args->actionButton, - args->flags, args->metaState, args->buttonState, - args->classification, args->edgeFlags, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->pointerCount, args->pointerProperties, - args->pointerCoords, 0, 0); - - needWake = enqueueInboundEventLocked(newEntry); - mLock.unlock(); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) { - return mInputFilterEnabled; -} - -void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " - "switchMask=0x%08x", - args->eventTime, args->policyFlags, args->switchValues, args->switchMask); -#endif - - uint32_t policyFlags = args->policyFlags; - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->notifySwitch(args->eventTime, - args->switchValues, args->switchMask, policyFlags); -} - -void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", - args->eventTime, args->deviceId); -#endif - - bool needWake; - { // acquire lock - std::scoped_lock _l(mLock); - - DeviceResetEntry* newEntry = - new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId); - needWake = enqueueInboundEventLocked(newEntry); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -int32_t InputDispatcher::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", - event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); -#endif - - nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); - - policyFlags |= POLICY_FLAG_INJECTED; - if (hasInjectionPermission(injectorPid, injectorUid)) { - policyFlags |= POLICY_FLAG_TRUSTED; - } - - std::queue injectedEntries; - switch (event->getType()) { - case AINPUT_EVENT_TYPE_KEY: { - KeyEvent keyEvent; - keyEvent.initialize(*static_cast(event)); - int32_t action = keyEvent.getAction(); - if (! validateKeyEvent(action)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - int32_t flags = keyEvent.getFlags(); - int32_t keyCode = keyEvent.getKeyCode(); - int32_t metaState = keyEvent.getMetaState(); - accelerateMetaShortcuts(keyEvent.getDeviceId(), action, - /*byref*/ keyCode, /*byref*/ metaState); - keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), - action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), - keyEvent.getDownTime(), keyEvent.getEventTime()); - - if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { - policyFlags |= POLICY_FLAG_VIRTUAL; - } - - if (!(policyFlags & POLICY_FLAG_FILTERED)) { - android::base::Timer t; - mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - } - - mLock.lock(); - KeyEntry* injectedEntry = - new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, keyEvent.getKeyCode(), - keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); - injectedEntries.push(injectedEntry); - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - const MotionEvent* motionEvent = static_cast(event); - int32_t action = motionEvent->getAction(); - size_t pointerCount = motionEvent->getPointerCount(); - const PointerProperties* pointerProperties = motionEvent->getPointerProperties(); - int32_t actionButton = motionEvent->getActionButton(); - int32_t displayId = motionEvent->getDisplayId(); - if (! validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - if (!(policyFlags & POLICY_FLAG_FILTERED)) { - nsecs_t eventTime = motionEvent->getEventTime(); - android::base::Timer t; - mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - } - - mLock.lock(); - const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); - const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - MotionEntry* injectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, actionButton, - motionEvent->getFlags(), motionEvent->getMetaState(), - motionEvent->getButtonState(), motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), - motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - injectedEntries.push(injectedEntry); - for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { - sampleEventTimes += 1; - samplePointerCoords += pointerCount; - MotionEntry* nextInjectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, actionButton, - motionEvent->getFlags(), motionEvent->getMetaState(), - motionEvent->getButtonState(), motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), - motionEvent->getRawXCursorPosition(), - motionEvent->getRawYCursorPosition(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - injectedEntries.push(nextInjectedEntry); - } - break; - } - - default: - ALOGW("Cannot inject event of type %d", event->getType()); - return INPUT_EVENT_INJECTION_FAILED; - } - - InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionState->injectionIsAsync = true; - } - - injectionState->refCount += 1; - injectedEntries.back()->injectionState = injectionState; - - bool needWake = false; - while (!injectedEntries.empty()) { - needWake |= enqueueInboundEventLocked(injectedEntries.front()); - injectedEntries.pop(); - } - - mLock.unlock(); - - if (needWake) { - mLooper->wake(); - } - - int32_t injectionResult; - { // acquire lock - std::unique_lock _l(mLock); - - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - } else { - for (;;) { - injectionResult = injectionState->injectionResult; - if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { - break; - } - - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Timed out waiting for injection result " - "to become available."); -#endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - break; - } - - mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); - } - - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED - && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { - while (injectionState->pendingForegroundDispatches != 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectionState->pendingForegroundDispatches); -#endif - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Timed out waiting for pending foreground " - "dispatches to finish."); -#endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - break; - } - - mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); - } - } - } - - injectionState->release(); - } // release lock - -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Finished with result %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectorPid, injectorUid); -#endif - - return injectionResult; -} - -bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { - return injectorUid == 0 - || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); -} - -void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { -#if DEBUG_INJECTION - ALOGD("Setting input event injection result to %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectionState->injectorPid, injectionState->injectorUid); -#endif - - if (injectionState->injectionIsAsync - && !(entry->policyFlags & POLICY_FLAG_FILTERED)) { - // Log the outcome since the injector did not wait for the injection result. - switch (injectionResult) { - case INPUT_EVENT_INJECTION_SUCCEEDED: - ALOGV("Asynchronous input event injection succeeded."); - break; - case INPUT_EVENT_INJECTION_FAILED: - ALOGW("Asynchronous input event injection failed."); - break; - case INPUT_EVENT_INJECTION_PERMISSION_DENIED: - ALOGW("Asynchronous input event injection permission denied."); - break; - case INPUT_EVENT_INJECTION_TIMED_OUT: - ALOGW("Asynchronous input event injection timed out."); - break; - } - } - - injectionState->injectionResult = injectionResult; - mInjectionResultAvailable.notify_all(); - } -} - -void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches += 1; - } -} - -void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches -= 1; - - if (injectionState->pendingForegroundDispatches == 0) { - mInjectionSyncFinished.notify_all(); - } - } -} - -std::vector> InputDispatcher::getWindowHandlesLocked( - int32_t displayId) const { - return getValueByKey(mWindowHandlesByDisplay, displayId); -} - -sp InputDispatcher::getWindowHandleLocked( - const sp& windowHandleToken) const { - for (auto& it : mWindowHandlesByDisplay) { - const std::vector> windowHandles = it.second; - for (const sp& windowHandle : windowHandles) { - if (windowHandle->getToken() == windowHandleToken) { - return windowHandle; - } - } - } - return nullptr; -} - -bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle) const { - for (auto& it : mWindowHandlesByDisplay) { - const std::vector> windowHandles = it.second; - for (const sp& handle : windowHandles) { - if (handle->getToken() == windowHandle->getToken()) { - if (windowHandle->getInfo()->displayId != it.first) { - ALOGE("Found window %s in display %" PRId32 - ", but it should belong to display %" PRId32, - windowHandle->getName().c_str(), it.first, - windowHandle->getInfo()->displayId); - } - return true; - } - } - } - return false; -} - -sp InputDispatcher::getInputChannelLocked(const sp& token) const { - size_t count = mInputChannelsByToken.count(token); - if (count == 0) { - return nullptr; - } - return mInputChannelsByToken.at(token); -} - -void InputDispatcher::updateWindowHandlesForDisplayLocked( - const std::vector>& inputWindowHandles, int32_t displayId) { - if (inputWindowHandles.empty()) { - // Remove all handles on a display if there are no windows left. - mWindowHandlesByDisplay.erase(displayId); - return; - } - - // Since we compare the pointer of input window handles across window updates, we need - // to make sure the handle object for the same window stays unchanged across updates. - const std::vector>& oldHandles = getWindowHandlesLocked(displayId); - std::unordered_map, sp, IBinderHash> oldHandlesByTokens; - for (const sp& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; - } - - std::vector> newHandles; - for (const sp& handle : inputWindowHandles) { - if (!handle->updateInfo()) { - // handle no longer valid - continue; - } - - const InputWindowInfo* info = handle->getInfo(); - if ((getInputChannelLocked(handle->getToken()) == nullptr && - info->portalToDisplayId == ADISPLAY_ID_NONE)) { - const bool noInputChannel = - info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; - const bool canReceiveInput = - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); - if (canReceiveInput && !noInputChannel) { - ALOGE("Window handle %s has no registered input channel", - handle->getName().c_str()); - } - continue; - } - - if (info->displayId != displayId) { - ALOGE("Window %s updated by wrong display %d, should belong to display %d", - handle->getName().c_str(), displayId, info->displayId); - continue; - } - - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); - oldHandle->updateFrom(handle); - newHandles.push_back(oldHandle); - } else { - newHandles.push_back(handle); - } - } - - // Insert or replace - mWindowHandlesByDisplay[displayId] = newHandles; -} - -/** - * Called from InputManagerService, update window handle list by displayId that can receive input. - * A window handle contains information about InputChannel, Touch Region, Types, Focused,... - * If set an empty list, remove all handles from the specific display. - * For focused handle, check if need to change and send a cancel event to previous one. - * For removed handle, check if need to send a cancel event if already in touch. - */ -void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, - int32_t displayId, const sp& setInputWindowsListener) { -#if DEBUG_FOCUS - ALOGD("setInputWindows displayId=%" PRId32, displayId); -#endif - { // acquire lock - std::scoped_lock _l(mLock); - - // Copy old handles for release if they are no longer present. - const std::vector> oldWindowHandles = - getWindowHandlesLocked(displayId); - - updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); - - sp newFocusedWindowHandle = nullptr; - bool foundHoveredWindow = false; - for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } - } - - if (!foundHoveredWindow) { - mLastHoverWindowHandle = nullptr; - } - - sp oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - - if (oldFocusedWindowHandle != newFocusedWindowHandle) { - if (oldFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); -#endif - sp focusedInputChannel = getInputChannelLocked( - oldFocusedWindowHandle->getToken()); - if (focusedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked( - focusedInputChannel, options); - } - mFocusedWindowHandlesByDisplay.erase(displayId); - } - if (newFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); -#endif - mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; - } - - if (mFocusedDisplayId == displayId) { - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); - } - - } - - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex >= 0) { - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - for (size_t i = 0; i < state.windows.size(); ) { - TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { -#if DEBUG_FOCUS - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); -#endif - sp touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked( - touchedInputChannel, options); - } - state.windows.erase(state.windows.begin() + i); - } else { - ++i; - } - } - } - - // Release information for windows that are no longer present. - // This ensures that unused input channels are released promptly. - // Otherwise, they might stick around until the window handle is destroyed - // which might not happen until the next GC. - for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { -#if DEBUG_FOCUS - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); -#endif - oldWindowHandle->releaseChannel(); - } - } - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); - - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); - } -} - -void InputDispatcher::setFocusedApplication( - int32_t displayId, const sp& inputApplicationHandle) { -#if DEBUG_FOCUS - ALOGD("setFocusedApplication displayId=%" PRId32, displayId); -#endif - { // acquire lock - std::scoped_lock _l(mLock); - - sp oldFocusedApplicationHandle = - getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); - if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { - if (oldFocusedApplicationHandle != inputApplicationHandle) { - if (oldFocusedApplicationHandle != nullptr) { - resetANRTimeoutsLocked(); - } - mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; - } - } else if (oldFocusedApplicationHandle != nullptr) { - resetANRTimeoutsLocked(); - oldFocusedApplicationHandle.clear(); - mFocusedApplicationHandlesByDisplay.erase(displayId); - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); -} - -/** - * Sets the focused display, which is responsible for receiving focus-dispatched input events where - * the display not specified. - * - * We track any unreleased events for each window. If a window loses the ability to receive the - * released event, we will send a cancel event to it. So when the focused display is changed, we - * cancel all the unreleased display-unspecified events for the focused window on the old focused - * display. The display-specified events won't be affected. - */ -void InputDispatcher::setFocusedDisplay(int32_t displayId) { -#if DEBUG_FOCUS - ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); -#endif - { // acquire lock - std::scoped_lock _l(mLock); - - if (mFocusedDisplayId != displayId) { - sp oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); - if (oldFocusedWindowHandle != nullptr) { - sp inputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); - if (inputChannel != nullptr) { - CancelationOptions options( - CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "The display which contains this window no longer has focus."); - options.displayId = ADISPLAY_ID_NONE; - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); - } - } - mFocusedDisplayId = displayId; - - // Sanity check - sp newFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); - - if (newFocusedWindowHandle == nullptr) { - ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId); - if (!mFocusedWindowHandlesByDisplay.empty()) { - ALOGE("But another display has a focused window:"); - for (auto& it : mFocusedWindowHandlesByDisplay) { - const int32_t displayId = it.first; - const sp& windowHandle = it.second; - ALOGE("Display #%" PRId32 " has focused window: '%s'\n", - displayId, windowHandle->getName().c_str()); - } - } - } - } - -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); -} - -void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { -#if DEBUG_FOCUS - ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); -#endif - - bool changed; - { // acquire lock - std::scoped_lock _l(mLock); - - if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { - if (mDispatchFrozen && !frozen) { - resetANRTimeoutsLocked(); - } - - if (mDispatchEnabled && !enabled) { - resetAndDropEverythingLocked("dispatcher is being disabled"); - } - - mDispatchEnabled = enabled; - mDispatchFrozen = frozen; - changed = true; - } else { - changed = false; - } - -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif - } // release lock - - if (changed) { - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); - } -} - -void InputDispatcher::setInputFilterEnabled(bool enabled) { -#if DEBUG_FOCUS - ALOGD("setInputFilterEnabled: enabled=%d", enabled); -#endif - - { // acquire lock - std::scoped_lock _l(mLock); - - if (mInputFilterEnabled == enabled) { - return; - } - - mInputFilterEnabled = enabled; - resetAndDropEverythingLocked("input filter is being enabled or disabled"); - } // release lock - - // Wake up poll loop since there might be work to do to drop everything. - mLooper->wake(); -} - -bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp& toToken) { - if (fromToken == toToken) { -#if DEBUG_FOCUS - ALOGD("Trivial transfer to same window."); -#endif - return true; - } - - { // acquire lock - std::scoped_lock _l(mLock); - - sp fromWindowHandle = getWindowHandleLocked(fromToken); - sp toWindowHandle = getWindowHandleLocked(toToken); - if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { - ALOGW("Cannot transfer focus because from or to window not found."); - return false; - } -#if DEBUG_FOCUS - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); -#endif - if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { -#if DEBUG_FOCUS - ALOGD("Cannot transfer focus because windows are on different displays."); -#endif - return false; - } - - bool found = false; - for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { - TouchState& state = mTouchStatesByDisplay.editValueAt(d); - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - if (touchedWindow.windowHandle == fromWindowHandle) { - int32_t oldTargetFlags = touchedWindow.targetFlags; - BitSet32 pointerIds = touchedWindow.pointerIds; - - state.windows.erase(state.windows.begin() + i); - - int32_t newTargetFlags = oldTargetFlags - & (InputTarget::FLAG_FOREGROUND - | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); - state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); - - found = true; - goto Found; - } - } - } -Found: - - if (! found) { -#if DEBUG_FOCUS - ALOGD("Focus transfer failed because from window did not have focus."); -#endif - return false; - } - - sp fromChannel = getInputChannelLocked(fromToken); - sp toChannel = getInputChannelLocked(toToken); - sp fromConnection = getConnectionLocked(fromChannel); - sp toConnection = getConnectionLocked(toChannel); - if (fromConnection != nullptr && toConnection != nullptr) { - fromConnection->inputState.copyPointerStateTo(toConnection->inputState); - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "transferring touch focus from this window to another window"); - synthesizeCancelationEventsForConnectionLocked(fromConnection, options); - } - -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); - return true; -} - -void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { -#if DEBUG_FOCUS - ALOGD("Resetting and dropping all events (%s).", reason); -#endif - - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - resetANRTimeoutsLocked(); - - mTouchStatesByDisplay.clear(); - mLastHoverWindowHandle.clear(); - mReplacedKeys.clear(); -} - -void InputDispatcher::logDispatchStateLocked() { - std::string dump; - dumpDispatchStateLocked(dump); - - std::istringstream stream(dump); - std::string line; - - while (std::getline(stream, line, '\n')) { - ALOGD("%s", line.c_str()); - } -} - -void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { - dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled)); - dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen)); - dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled)); - dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId); - - if (!mFocusedApplicationHandlesByDisplay.empty()) { - dump += StringPrintf(INDENT "FocusedApplications:\n"); - for (auto& it : mFocusedApplicationHandlesByDisplay) { - const int32_t displayId = it.first; - const sp& applicationHandle = it.second; - dump += StringPrintf( - INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n", - displayId, - applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0); - } - } else { - dump += StringPrintf(INDENT "FocusedApplications: \n"); - } - - if (!mFocusedWindowHandlesByDisplay.empty()) { - dump += StringPrintf(INDENT "FocusedWindows:\n"); - for (auto& it : mFocusedWindowHandlesByDisplay) { - const int32_t displayId = it.first; - const sp& windowHandle = it.second; - dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", - displayId, windowHandle->getName().c_str()); - } - } else { - dump += StringPrintf(INDENT "FocusedWindows: \n"); - } - - if (!mTouchStatesByDisplay.isEmpty()) { - dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); - for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) { - const TouchState& state = mTouchStatesByDisplay.valueAt(i); - dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", - state.displayId, toString(state.down), toString(state.split), - state.deviceId, state.source); - if (!state.windows.empty()) { - dump += INDENT3 "Windows:\n"; - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.windowHandle->getName().c_str(), - touchedWindow.pointerIds.value, - touchedWindow.targetFlags); - } - } else { - dump += INDENT3 "Windows: \n"; - } - if (!state.portalWindows.empty()) { - dump += INDENT3 "Portal windows:\n"; - for (size_t i = 0; i < state.portalWindows.size(); i++) { - const sp portalWindowHandle = state.portalWindows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s'\n", - i, portalWindowHandle->getName().c_str()); - } - } - } - } else { - dump += INDENT "TouchStates: \n"; - } - - if (!mWindowHandlesByDisplay.empty()) { - for (auto& it : mWindowHandlesByDisplay) { - const std::vector> windowHandles = it.second; - dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); - if (!windowHandles.empty()) { - dump += INDENT2 "Windows:\n"; - for (size_t i = 0; i < windowHandles.size(); i++) { - const sp& windowHandle = windowHandles[i]; - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - - dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " - "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), " - "touchableRegion=", - i, windowInfo->name.c_str(), windowInfo->displayId, - windowInfo->portalToDisplayId, - toString(windowInfo->paused), - toString(windowInfo->hasFocus), - toString(windowInfo->hasWallpaper), - toString(windowInfo->visible), - toString(windowInfo->canReceiveKeys), - windowInfo->layoutParamsFlags, windowInfo->layoutParamsType, - windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->globalScaleFactor, - windowInfo->windowXScale, windowInfo->windowYScale); - dumpRegion(dump, windowInfo->touchableRegion); - dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); - } - } else { - dump += INDENT2 "Windows: \n"; - } - } - } else { - dump += INDENT "Displays: \n"; - } - - if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) { - for (auto& it : mGlobalMonitorsByDisplay) { - const std::vector& monitors = it.second; - dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first); - dumpMonitors(dump, monitors); - } - for (auto& it : mGestureMonitorsByDisplay) { - const std::vector& monitors = it.second; - dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first); - dumpMonitors(dump, monitors); - } - } else { - dump += INDENT "Monitors: \n"; - } - - nsecs_t currentTime = now(); - - // Dump recently dispatched or dropped events from oldest to newest. - if (!mRecentQueue.empty()) { - dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); - for (EventEntry* entry : mRecentQueue) { - dump += INDENT2; - entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - entry->eventTime) * 0.000001f); - } - } else { - dump += INDENT "RecentQueue: \n"; - } - - // Dump event currently being dispatched. - if (mPendingEvent) { - dump += INDENT "PendingEvent:\n"; - dump += INDENT2; - mPendingEvent->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); - } else { - dump += INDENT "PendingEvent: \n"; - } - - // Dump inbound events from oldest to newest. - if (!mInboundQueue.empty()) { - dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size()); - for (EventEntry* entry : mInboundQueue) { - dump += INDENT2; - entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - entry->eventTime) * 0.000001f); - } - } else { - dump += INDENT "InboundQueue: \n"; - } - - if (!mReplacedKeys.isEmpty()) { - dump += INDENT "ReplacedKeys:\n"; - for (size_t i = 0; i < mReplacedKeys.size(); i++) { - const KeyReplacement& replacement = mReplacedKeys.keyAt(i); - int32_t newKeyCode = mReplacedKeys.valueAt(i); - dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", - i, replacement.keyCode, replacement.deviceId, newKeyCode); - } - } else { - dump += INDENT "ReplacedKeys: \n"; - } - - if (!mConnectionsByFd.empty()) { - dump += INDENT "Connections:\n"; - for (const auto& pair : mConnectionsByFd) { - const sp& connection = pair.second; - dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", - pair.first, connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), connection->getStatusLabel(), - toString(connection->monitor), - toString(connection->inputPublisherBlocked)); - - if (!connection->outboundQueue.empty()) { - dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", - connection->outboundQueue.size()); - for (DispatchEntry* entry : connection->outboundQueue) { - dump.append(INDENT4); - entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", - entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); - } - } else { - dump += INDENT3 "OutboundQueue: \n"; - } - - if (!connection->waitQueue.empty()) { - dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", - connection->waitQueue.size()); - for (DispatchEntry* entry : connection->waitQueue) { - dump += INDENT4; - entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", - entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); - } - } else { - dump += INDENT3 "WaitQueue: \n"; - } - } - } else { - dump += INDENT "Connections: \n"; - } - - if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); - } else { - dump += INDENT "AppSwitch: not pending\n"; - } - - dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", - mConfig.keyRepeatDelay * 0.000001f); - dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); -} - -void InputDispatcher::dumpMonitors(std::string& dump, const std::vector& monitors) { - const size_t numMonitors = monitors.size(); - for (size_t i = 0; i < numMonitors; i++) { - const Monitor& monitor = monitors[i]; - const sp& channel = monitor.inputChannel; - dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); - dump += "\n"; - } -} - -status_t InputDispatcher::registerInputChannel(const sp& inputChannel, - int32_t displayId) { -#if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, - inputChannel->getName().c_str(), displayId); -#endif - - { // acquire lock - std::scoped_lock _l(mLock); - sp existingConnection = getConnectionLocked(inputChannel); - if (existingConnection != nullptr) { - ALOGW("Attempted to register already registered input channel '%s'", - inputChannel->getName().c_str()); - return BAD_VALUE; - } - - sp connection = new Connection(inputChannel, false /*monitor*/); - - int fd = inputChannel->getFd(); - mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; - - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); - } // release lock - - // Wake the looper because some connections have changed. - mLooper->wake(); - return OK; -} - -status_t InputDispatcher::registerInputMonitor(const sp& inputChannel, - int32_t displayId, bool isGestureMonitor) { - { // acquire lock - std::scoped_lock _l(mLock); - - if (displayId < 0) { - ALOGW("Attempted to register input monitor without a specified display."); - return BAD_VALUE; - } - - if (inputChannel->getToken() == nullptr) { - ALOGW("Attempted to register input monitor without an identifying token."); - return BAD_VALUE; - } - - sp connection = new Connection(inputChannel, true /*monitor*/); - - const int fd = inputChannel->getFd(); - mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; - - auto& monitorsByDisplay = isGestureMonitor - ? mGestureMonitorsByDisplay - : mGlobalMonitorsByDisplay; - monitorsByDisplay[displayId].emplace_back(inputChannel); - - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); - - } - // Wake the looper because some connections have changed. - mLooper->wake(); - return OK; -} - -status_t InputDispatcher::unregisterInputChannel(const sp& inputChannel) { -#if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str()); -#endif - - { // acquire lock - std::scoped_lock _l(mLock); - - status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/); - if (status) { - return status; - } - } // release lock - - // Wake the poll loop because removing the connection may have changed the current - // synchronization state. - mLooper->wake(); - return OK; -} - -status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, - bool notify) { - sp connection = getConnectionLocked(inputChannel); - if (connection == nullptr) { - ALOGW("Attempted to unregister already unregistered input channel '%s'", - inputChannel->getName().c_str()); - return BAD_VALUE; - } - - const bool removed = removeByValue(mConnectionsByFd, connection); - ALOG_ASSERT(removed); - mInputChannelsByToken.erase(inputChannel->getToken()); - - if (connection->monitor) { - removeMonitorChannelLocked(inputChannel); - } - - mLooper->removeFd(inputChannel->getFd()); - - nsecs_t currentTime = now(); - abortBrokenDispatchCycleLocked(currentTime, connection, notify); - - connection->status = Connection::STATUS_ZOMBIE; - return OK; -} - -void InputDispatcher::removeMonitorChannelLocked(const sp& inputChannel) { - removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay); - removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay); -} - -void InputDispatcher::removeMonitorChannelLocked(const sp& inputChannel, - std::unordered_map>& monitorsByDisplay) { - for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) { - std::vector& monitors = it->second; - const size_t numMonitors = monitors.size(); - for (size_t i = 0; i < numMonitors; i++) { - if (monitors[i].inputChannel == inputChannel) { - monitors.erase(monitors.begin() + i); - break; - } - } - if (monitors.empty()) { - it = monitorsByDisplay.erase(it); - } else { - ++it; - } - } -} - -status_t InputDispatcher::pilferPointers(const sp& token) { - { // acquire lock - std::scoped_lock _l(mLock); - std::optional foundDisplayId = findGestureMonitorDisplayByTokenLocked(token); - - if (!foundDisplayId) { - ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token"); - return BAD_VALUE; - } - int32_t displayId = foundDisplayId.value(); - - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex < 0) { - ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId); - return BAD_VALUE; - } - - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - std::optional foundDeviceId; - for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { - if (touchedMonitor.monitor.inputChannel->getToken() == token) { - foundDeviceId = state.deviceId; - } - } - if (!foundDeviceId || !state.down) { - ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams." - " Ignoring."); - return BAD_VALUE; - } - int32_t deviceId = foundDeviceId.value(); - - // Send cancel events to all the input channels we're stealing from. - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "gesture monitor stole pointer stream"); - options.deviceId = deviceId; - options.displayId = displayId; - for (const TouchedWindow& window : state.windows) { - sp channel = getInputChannelLocked(window.windowHandle->getToken()); - synthesizeCancelationEventsForInputChannelLocked(channel, options); - } - // Then clear the current touch state so we stop dispatching to them as well. - state.filterNonMonitors(); - } - return OK; -} - - -std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( - const sp& token) { - for (const auto& it : mGestureMonitorsByDisplay) { - const std::vector& monitors = it.second; - for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getToken() == token) { - return it.first; - } - } - } - return std::nullopt; -} - -sp InputDispatcher::getConnectionLocked( - const sp& inputChannel) { - if (inputChannel == nullptr) { - return nullptr; - } - - for (const auto& pair : mConnectionsByFd) { - sp connection = pair.second; - if (connection->inputChannel->getToken() == inputChannel->getToken()) { - return connection; - } - } - - return nullptr; -} - -void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); - commandEntry->connection = connection; - commandEntry->eventTime = currentTime; - commandEntry->seq = seq; - commandEntry->handled = handled; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp& connection) { - ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", - connection->getInputChannelName().c_str()); - - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->connection = connection; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::onFocusChangedLocked(const sp& oldFocus, - const sp& newFocus) { - sp oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; - sp newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; - std::unique_ptr commandEntry = std::make_unique( - &InputDispatcher::doNotifyFocusChangedLockedInterruptible); - commandEntry->oldToken = oldToken; - commandEntry->newToken = newToken; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::onANRLocked( - nsecs_t currentTime, const sp& applicationHandle, - const sp& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) { - float dispatchLatency = (currentTime - eventTime) * 0.000001f; - float waitDuration = (currentTime - waitStartTime) * 0.000001f; - ALOGI("Application is not responding: %s. " - "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), - dispatchLatency, waitDuration, reason); - - // Capture a record of the InputDispatcher state at the time of the ANR. - time_t t = time(nullptr); - struct tm tm; - localtime_r(&t, &tm); - char timestr[64]; - strftime(timestr, sizeof(timestr), "%F %T", &tm); - mLastANRState.clear(); - mLastANRState += INDENT "ANR:\n"; - mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastANRState += StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); - mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); - mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); - mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); - dumpDispatchStateLocked(mLastANRState); - - std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doNotifyANRLockedInterruptible); - commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputChannel = windowHandle != nullptr ? - getInputChannelLocked(windowHandle->getToken()) : nullptr; - commandEntry->reason = reason; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible ( - CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyConfigurationChanged(commandEntry->eventTime); - - mLock.lock(); -} - -void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( - CommandEntry* commandEntry) { - sp connection = commandEntry->connection; - - if (connection->status != Connection::STATUS_ZOMBIE) { - mLock.unlock(); - - mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken()); - - mLock.lock(); - } -} - -void InputDispatcher::doNotifyFocusChangedLockedInterruptible( - CommandEntry* commandEntry) { - sp oldToken = commandEntry->oldToken; - sp newToken = commandEntry->newToken; - mLock.unlock(); - mPolicy->notifyFocusChanged(oldToken, newToken); - mLock.lock(); -} - -void InputDispatcher::doNotifyANRLockedInterruptible( - CommandEntry* commandEntry) { - mLock.unlock(); - - nsecs_t newTimeout = mPolicy->notifyANR( - commandEntry->inputApplicationHandle, - commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr, - commandEntry->reason); - - mLock.lock(); - - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, - commandEntry->inputChannel); -} - -void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( - CommandEntry* commandEntry) { - KeyEntry* entry = commandEntry->keyEntry; - - KeyEvent event; - initializeKeyEvent(&event, entry); - - mLock.unlock(); - - android::base::Timer t; - sp token = commandEntry->inputChannel != nullptr ? - commandEntry->inputChannel->getToken() : nullptr; - nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, - &event, entry->policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - - mLock.lock(); - - if (delay < 0) { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; - } else if (!delay) { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; - } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; - entry->interceptKeyWakeupTime = now() + delay; - } - entry->release(); -} - -void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - mPolicy->onPointerDownOutsideFocus(commandEntry->newToken); - mLock.lock(); -} - -void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( - CommandEntry* commandEntry) { - sp connection = commandEntry->connection; - const nsecs_t finishTime = commandEntry->eventTime; - uint32_t seq = commandEntry->seq; - const bool handled = commandEntry->handled; - - // Handle post-event policy actions. - std::deque::iterator dispatchEntryIt = - connection->findWaitQueueEntry(seq); - if (dispatchEntryIt == connection->waitQueue.end()) { - return; - } - DispatchEntry* dispatchEntry = *dispatchEntryIt; - - nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; - if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); - } - - bool restartEvent; - if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); - restartEvent = - afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); - restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, - handled); - } else { - restartEvent = false; - } - - // Dequeue the event and start the next cycle. - // Note that because the lock might have been released, it is possible that the - // contents of the wait queue to have been drained, so we need to double-check - // a few things. - dispatchEntryIt = connection->findWaitQueueEntry(seq); - if (dispatchEntryIt != connection->waitQueue.end()) { - dispatchEntry = *dispatchEntryIt; - connection->waitQueue.erase(dispatchEntryIt); - traceWaitQueueLength(connection); - if (restartEvent && connection->status == Connection::STATUS_NORMAL) { - connection->outboundQueue.push_front(dispatchEntry); - traceOutboundQueueLength(connection); - } else { - releaseDispatchEntry(dispatchEntry); - } - } - - // Start the next dispatch cycle for this connection. - startDispatchCycleLocked(now(), connection); -} - -bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) { - if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) { - if (!handled) { - // Report the key as unhandled, since the fallback was not handled. - mReporter->reportUnhandledKey(keyEntry->sequenceNum); - } - return false; - } - - // Get the fallback key state. - // Clear it out after dispatching the UP. - int32_t originalKeyCode = keyEntry->keyCode; - int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode); - if (keyEntry->action == AKEY_EVENT_ACTION_UP) { - connection->inputState.removeFallbackKey(originalKeyCode); - } - - if (handled || !dispatchEntry->hasForegroundTarget()) { - // If the application handles the original key for which we previously - // generated a fallback or if the window is not a foreground window, - // then cancel the associated fallback key, if any. - if (fallbackKeyCode != -1) { - // Dispatch the unhandled key to the policy with the cancel flag. -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Asking policy to cancel fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); -#endif - KeyEvent event; - initializeKeyEvent(&event, keyEntry); - event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); - - mLock.unlock(); - - mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), - &event, keyEntry->policyFlags, &event); - - mLock.lock(); - - // Cancel the fallback key. - if (fallbackKeyCode != AKEYCODE_UNKNOWN) { - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, - "application handled the original non-fallback key " - "or is no longer a foreground target, " - "canceling previously dispatched fallback key"); - options.keyCode = fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); - } - connection->inputState.removeFallbackKey(originalKeyCode); - } - } else { - // If the application did not handle a non-fallback key, first check - // that we are in a good state to perform unhandled key event processing - // Then ask the policy what to do with it. - bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN - && keyEntry->repeatCount == 0; - if (fallbackKeyCode == -1 && !initialDown) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Skipping unhandled key event processing " - "since this is not an initial down. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - originalKeyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); -#endif - return false; - } - - // Dispatch the unhandled key to the policy. -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Asking policy to perform fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); -#endif - KeyEvent event; - initializeKeyEvent(&event, keyEntry); - - mLock.unlock(); - - bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), - &event, keyEntry->policyFlags, &event); - - mLock.lock(); - - if (connection->status != Connection::STATUS_NORMAL) { - connection->inputState.removeFallbackKey(originalKeyCode); - return false; - } - - // Latch the fallback keycode for this key on an initial down. - // The fallback keycode cannot change at any other point in the lifecycle. - if (initialDown) { - if (fallback) { - fallbackKeyCode = event.getKeyCode(); - } else { - fallbackKeyCode = AKEYCODE_UNKNOWN; - } - connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode); - } - - ALOG_ASSERT(fallbackKeyCode != -1); - - // Cancel the fallback key if the policy decides not to send it anymore. - // We will continue to dispatch the key to the policy but we will no - // longer dispatch a fallback key to the application. - if (fallbackKeyCode != AKEYCODE_UNKNOWN - && (!fallback || fallbackKeyCode != event.getKeyCode())) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - if (fallback) { - ALOGD("Unhandled key event: Policy requested to send key %d" - "as a fallback for %d, but on the DOWN it had requested " - "to send %d instead. Fallback canceled.", - event.getKeyCode(), originalKeyCode, fallbackKeyCode); - } else { - ALOGD("Unhandled key event: Policy did not request fallback for %d, " - "but on the DOWN it had requested to send %d. " - "Fallback canceled.", - originalKeyCode, fallbackKeyCode); - } -#endif - - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, - "canceling fallback, policy no longer desires it"); - options.keyCode = fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); - - fallback = false; - fallbackKeyCode = AKEYCODE_UNKNOWN; - if (keyEntry->action != AKEY_EVENT_ACTION_UP) { - connection->inputState.setFallbackKey(originalKeyCode, - fallbackKeyCode); - } - } - -#if DEBUG_OUTBOUND_EVENT_DETAILS - { - std::string msg; - const KeyedVector& fallbackKeys = - connection->inputState.getFallbackKeys(); - for (size_t i = 0; i < fallbackKeys.size(); i++) { - msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), - fallbackKeys.valueAt(i)); - } - ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.", - fallbackKeys.size(), msg.c_str()); - } -#endif - - if (fallback) { - // Restart the dispatch cycle using the fallback key. - keyEntry->eventTime = event.getEventTime(); - keyEntry->deviceId = event.getDeviceId(); - keyEntry->source = event.getSource(); - keyEntry->displayId = event.getDisplayId(); - keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; - keyEntry->keyCode = fallbackKeyCode; - keyEntry->scanCode = event.getScanCode(); - keyEntry->metaState = event.getMetaState(); - keyEntry->repeatCount = event.getRepeatCount(); - keyEntry->downTime = event.getDownTime(); - keyEntry->syntheticRepeat = false; - -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Dispatching fallback key. " - "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", - originalKeyCode, fallbackKeyCode, keyEntry->metaState); -#endif - return true; // restart the event - } else { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: No fallback key."); -#endif - - // Report the key as unhandled, since there is no fallback key. - mReporter->reportUnhandledKey(keyEntry->sequenceNum); - } - } - return false; -} - -bool InputDispatcher::afterMotionEventLockedInterruptible(const sp& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) { - return false; -} - -void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType); - - mLock.lock(); -} - -void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) { - event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); -} - -void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { - // TODO Write some statistics about how long we spend waiting. -} - -void InputDispatcher::traceInboundQueueLengthLocked() { - if (ATRACE_ENABLED()) { - ATRACE_INT("iq", mInboundQueue.size()); - } -} - -void InputDispatcher::traceOutboundQueueLength(const sp& connection) { - if (ATRACE_ENABLED()) { - char counterName[40]; - snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->outboundQueue.size()); - } -} - -void InputDispatcher::traceWaitQueueLength(const sp& connection) { - if (ATRACE_ENABLED()) { - char counterName[40]; - snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->waitQueue.size()); - } -} - -void InputDispatcher::dump(std::string& dump) { - std::scoped_lock _l(mLock); - - dump += "Input Dispatcher State:\n"; - dumpDispatchStateLocked(dump); - - if (!mLastANRState.empty()) { - dump += "\nInput Dispatcher State at time of last ANR:\n"; - dump += mLastANRState; - } -} - -void InputDispatcher::monitor() { - // Acquire and release the lock to ensure that the dispatcher has not deadlocked. - std::unique_lock _l(mLock); - mLooper->wake(); - mDispatcherIsAlive.wait(_l); -} - - -// --- InputDispatcher::InjectionState --- - -InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) : - refCount(1), - injectorPid(injectorPid), injectorUid(injectorUid), - injectionResult(INPUT_EVENT_INJECTION_PENDING), injectionIsAsync(false), - pendingForegroundDispatches(0) { -} - -InputDispatcher::InjectionState::~InjectionState() { -} - -void InputDispatcher::InjectionState::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - - -// --- InputDispatcher::EventEntry --- - -InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, - nsecs_t eventTime, uint32_t policyFlags) : - sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime), - policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) { -} - -InputDispatcher::EventEntry::~EventEntry() { - releaseInjectionState(); -} - -void InputDispatcher::EventEntry::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - -void InputDispatcher::EventEntry::releaseInjectionState() { - if (injectionState) { - injectionState->release(); - injectionState = nullptr; - } -} - - -// --- InputDispatcher::ConfigurationChangedEntry --- - -InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry( - uint32_t sequenceNum, nsecs_t eventTime) : - EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) { -} - -InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() { -} - -void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); -} - - -// --- InputDispatcher::DeviceResetEntry --- - -InputDispatcher::DeviceResetEntry::DeviceResetEntry( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) : - EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), - deviceId(deviceId) { -} - -InputDispatcher::DeviceResetEntry::~DeviceResetEntry() { -} - -void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", - deviceId, policyFlags); -} - - -// --- InputDispatcher::KeyEntry --- - -InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - int32_t repeatCount, nsecs_t downTime) : - EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), - deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags), - keyCode(keyCode), scanCode(scanCode), metaState(metaState), - repeatCount(repeatCount), downTime(downTime), - syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), - interceptKeyWakeupTime(0) { -} - -InputDispatcher::KeyEntry::~KeyEntry() { -} - -void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " - "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " - "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode, - scanCode, metaState, repeatCount, policyFlags); -} - -void InputDispatcher::KeyEntry::recycle() { - releaseInjectionState(); - - dispatchInProgress = false; - syntheticRepeat = false; - interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; - interceptKeyWakeupTime = 0; -} - - -// --- InputDispatcher::MotionEntry --- - -InputDispatcher::MotionEntry::MotionEntry( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset) - : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), - eventTime(eventTime), - deviceId(deviceId), - source(source), - displayId(displayId), - action(action), - actionButton(actionButton), - flags(flags), - metaState(metaState), - buttonState(buttonState), - classification(classification), - edgeFlags(edgeFlags), - xPrecision(xPrecision), - yPrecision(yPrecision), - xCursorPosition(xCursorPosition), - yCursorPosition(yCursorPosition), - downTime(downTime), - pointerCount(pointerCount) { - for (uint32_t i = 0; i < pointerCount; i++) { - this->pointerProperties[i].copyFrom(pointerProperties[i]); - this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } - } -} - -InputDispatcher::MotionEntry::~MotionEntry() { -} - -void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 - ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " - "buttonState=0x%08x, " - "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " - "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), - actionButton, flags, metaState, buttonState, - motionClassificationToString(classification), edgeFlags, xPrecision, - yPrecision, xCursorPosition, yCursorPosition); - - for (uint32_t i = 0; i < pointerCount; i++) { - if (i) { - msg += ", "; - } - msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, - pointerCoords[i].getX(), pointerCoords[i].getY()); - } - msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); -} - - -// --- InputDispatcher::DispatchEntry --- - -volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; - -InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor, - float windowXScale, float windowYScale) : - seq(nextSeq()), - eventEntry(eventEntry), targetFlags(targetFlags), - xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor), - windowXScale(windowXScale), windowYScale(windowYScale), - deliveryTime(0), resolvedAction(0), resolvedFlags(0) { - eventEntry->refCount += 1; -} - -InputDispatcher::DispatchEntry::~DispatchEntry() { - eventEntry->release(); -} - -uint32_t InputDispatcher::DispatchEntry::nextSeq() { - // Sequence number 0 is reserved and will never be returned. - uint32_t seq; - do { - seq = android_atomic_inc(&sNextSeqAtomic); - } while (!seq); - return seq; -} - - -// --- InputDispatcher::InputState --- - -InputDispatcher::InputState::InputState() { -} - -InputDispatcher::InputState::~InputState() { -} - -bool InputDispatcher::InputState::isNeutral() const { - return mKeyMementos.empty() && mMotionMementos.empty(); -} - -bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source, - int32_t displayId) const { - for (const MotionMemento& memento : mMotionMementos) { - if (memento.deviceId == deviceId - && memento.source == source - && memento.displayId == displayId - && memento.hovering) { - return true; - } - } - return false; -} - -bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, - int32_t action, int32_t flags) { - switch (action) { - case AKEY_EVENT_ACTION_UP: { - if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { - for (size_t i = 0; i < mFallbackKeys.size(); ) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { - mFallbackKeys.removeItemsAt(i); - } else { - i += 1; - } - } - } - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - return true; - } - /* FIXME: We can't just drop the key up event because that prevents creating - * popup windows that are automatically shown when a key is held and then - * dismissed when the key is released. The problem is that the popup will - * not have received the original key down, so the key up will be considered - * to be inconsistent with its observed state. We could perhaps handle this - * by synthesizing a key down but that will cause other problems. - * - * So for now, allow inconsistent key up events to be dispatched. - * -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " - "keyCode=%d, scanCode=%d", - entry->deviceId, entry->source, entry->keyCode, entry->scanCode); -#endif - return false; - */ - return true; - } - - case AKEY_EVENT_ACTION_DOWN: { - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - } - addKeyMemento(entry, flags); - return true; - } - - default: - return true; - } -} - -bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, - int32_t action, int32_t flags) { - int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; - switch (actionMasked) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " - "displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, false /*hovering*/); - return true; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_MOVE: { - if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { - // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to - // generate cancellation events for these since they're based in relative rather than - // absolute units. - return true; - } - - ssize_t index = findMotionMemento(entry, false /*hovering*/); - - if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { - // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all - // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any - // other value and we need to track the motion so we can send cancellation events for - // anything generating fallback events (e.g. DPad keys for joystick movements). - if (index >= 0) { - if (entry->pointerCoords[0].isEmpty()) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } else { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - } - } else if (!entry->pointerCoords[0].isEmpty()) { - addMotionMemento(entry, flags, false /*hovering*/); - } - - // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. - return true; - } - if (index >= 0) { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion pointer up/down or move event: " - "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_EXIT: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " - "displayId=%" PRId32, - entry->deviceId, entry->source, entry->displayId); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, true /*hovering*/); - return true; - } - - default: - return true; - } -} - -ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { - for (size_t i = 0; i < mKeyMementos.size(); i++) { - const KeyMemento& memento = mKeyMementos[i]; - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.displayId == entry->displayId - && memento.keyCode == entry->keyCode - && memento.scanCode == entry->scanCode) { - return i; - } - } - return -1; -} - -ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, - bool hovering) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.displayId == entry->displayId - && memento.hovering == hovering) { - return i; - } - } - return -1; -} - -void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { - KeyMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.metaState = entry->metaState; - memento.flags = flags; - memento.downTime = entry->downTime; - memento.policyFlags = entry->policyFlags; - mKeyMementos.push_back(memento); -} - -void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, - int32_t flags, bool hovering) { - MotionMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.flags = flags; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.xCursorPosition = entry->xCursorPosition; - memento.yCursorPosition = entry->yCursorPosition; - memento.downTime = entry->downTime; - memento.setPointers(entry); - memento.hovering = hovering; - memento.policyFlags = entry->policyFlags; - mMotionMementos.push_back(memento); -} - -void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { - pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < entry->pointerCount; i++) { - pointerProperties[i].copyFrom(entry->pointerProperties[i]); - pointerCoords[i].copyFrom(entry->pointerCoords[i]); - } -} - -void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, - std::vector& outEvents, const CancelationOptions& options) { - for (KeyMemento& memento : mKeyMementos) { - if (shouldCancelKey(memento, options)) { - outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, memento.policyFlags, - AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime)); - } - } - - for (const MotionMemento& memento : mMotionMementos) { - if (shouldCancelMotion(memento, options)) { - const int32_t action = memento.hovering ? - AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back( - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, action, - 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, memento.pointerCount, - memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); - } - } -} - -void InputDispatcher::InputState::clear() { - mKeyMementos.clear(); - mMotionMementos.clear(); - mFallbackKeys.clear(); -} - -void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { - for (size_t j = 0; j < other.mMotionMementos.size(); ) { - const MotionMemento& otherMemento = other.mMotionMementos[j]; - if (memento.deviceId == otherMemento.deviceId - && memento.source == otherMemento.source - && memento.displayId == otherMemento.displayId) { - other.mMotionMementos.erase(other.mMotionMementos.begin() + j); - } else { - j += 1; - } - } - other.mMotionMementos.push_back(memento); - } - } -} - -int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - return index >= 0 ? mFallbackKeys.valueAt(index) : -1; -} - -void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode, - int32_t fallbackKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - if (index >= 0) { - mFallbackKeys.replaceValueAt(index, fallbackKeyCode); - } else { - mFallbackKeys.add(originalKeyCode, fallbackKeyCode); - } -} - -void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) { - mFallbackKeys.removeItem(originalKeyCode); -} - -bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, - const CancelationOptions& options) { - if (options.keyCode && memento.keyCode != options.keyCode.value()) { - return false; - } - - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return true; - case CancelationOptions::CANCEL_FALLBACK_EVENTS: - return memento.flags & AKEY_EVENT_FLAG_FALLBACK; - default: - return false; - } -} - -bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options) { - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - return true; - case CancelationOptions::CANCEL_POINTER_EVENTS: - return memento.source & AINPUT_SOURCE_CLASS_POINTER; - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); - default: - return false; - } -} - - -// --- InputDispatcher::Connection --- - -InputDispatcher::Connection::Connection(const sp& inputChannel, bool monitor) : - status(STATUS_NORMAL), inputChannel(inputChannel), - monitor(monitor), - inputPublisher(inputChannel), inputPublisherBlocked(false) { -} - -InputDispatcher::Connection::~Connection() { -} - -const std::string InputDispatcher::Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} - -const char* InputDispatcher::Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - - case STATUS_BROKEN: - return "BROKEN"; - - case STATUS_ZOMBIE: - return "ZOMBIE"; - - default: - return "UNKNOWN"; - } -} - -std::deque::iterator -InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (std::deque::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { - if ((*it)->seq == seq) { - return it; - } - } - return waitQueue.end(); -} - -// --- InputDispatcher::Monitor -InputDispatcher::Monitor::Monitor(const sp& inputChannel) : - inputChannel(inputChannel) { -} - - -// --- InputDispatcher::CommandEntry --- -// -InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0), - seq(0), handled(false) { -} - -InputDispatcher::CommandEntry::~CommandEntry() { -} - -// --- InputDispatcher::TouchedMonitor --- -InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, - float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) { -} - -// --- InputDispatcher::TouchState --- - -InputDispatcher::TouchState::TouchState() : - down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) { -} - -InputDispatcher::TouchState::~TouchState() { -} - -void InputDispatcher::TouchState::reset() { - down = false; - split = false; - deviceId = -1; - source = 0; - displayId = ADISPLAY_ID_NONE; - windows.clear(); - portalWindows.clear(); - gestureMonitors.clear(); -} - -void InputDispatcher::TouchState::copyFrom(const TouchState& other) { - down = other.down; - split = other.split; - deviceId = other.deviceId; - source = other.source; - displayId = other.displayId; - windows = other.windows; - portalWindows = other.portalWindows; - gestureMonitors = other.gestureMonitors; -} - -void InputDispatcher::TouchState::addOrUpdateWindow(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows[i]; - if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.targetFlags |= targetFlags; - if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; - } - touchedWindow.pointerIds.value |= pointerIds.value; - return; - } - } - - TouchedWindow touchedWindow; - touchedWindow.windowHandle = windowHandle; - touchedWindow.targetFlags = targetFlags; - touchedWindow.pointerIds = pointerIds; - windows.push_back(touchedWindow); -} - -void InputDispatcher::TouchState::addPortalWindow(const sp& windowHandle) { - size_t numWindows = portalWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - if (portalWindows[i] == windowHandle) { - return; - } - } - portalWindows.push_back(windowHandle); -} - -void InputDispatcher::TouchState::addGestureMonitors( - const std::vector& newMonitors) { - const size_t newSize = gestureMonitors.size() + newMonitors.size(); - gestureMonitors.reserve(newSize); - gestureMonitors.insert(std::end(gestureMonitors), - std::begin(newMonitors), std::end(newMonitors)); -} - -void InputDispatcher::TouchState::removeWindow(const sp& windowHandle) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle == windowHandle) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::removeWindowByToken(const sp& token) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle->getToken() == token) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { - for (size_t i = 0 ; i < windows.size(); ) { - TouchedWindow& window = windows[i]; - if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS - | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { - window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; - window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; - i += 1; - } else { - windows.erase(windows.begin() + i); - } - } -} - -void InputDispatcher::TouchState::filterNonMonitors() { - windows.clear(); - portalWindows.clear(); -} - -sp InputDispatcher::TouchState::getFirstForegroundWindowHandle() const { - for (size_t i = 0; i < windows.size(); i++) { - const TouchedWindow& window = windows[i]; - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - return window.windowHandle; - } - } - return nullptr; -} - -bool InputDispatcher::TouchState::isSlippery() const { - // Must have exactly one foreground window. - bool haveSlipperyForegroundWindow = false; - for (const TouchedWindow& window : windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - if (haveSlipperyForegroundWindow - || !(window.windowHandle->getInfo()->layoutParamsFlags - & InputWindowInfo::FLAG_SLIPPERY)) { - return false; - } - haveSlipperyForegroundWindow = true; - } - } - return haveSlipperyForegroundWindow; -} - - -// --- InputDispatcherThread --- - -InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) : - Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { -} - -InputDispatcherThread::~InputDispatcherThread() { -} - -bool InputDispatcherThread::threadLoop() { - mDispatcher->dispatchOnce(); - return true; -} - -} // namespace android diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h deleted file mode 100644 index e35ba2756b..0000000000 --- a/services/inputflinger/InputDispatcher.h +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_DISPATCHER_H -#define _UI_INPUT_DISPATCHER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "InputListener.h" -#include "InputReporterInterface.h" - -namespace android { - -/* - * Constants used to report the outcome of input event injection. - */ -enum { - /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ - INPUT_EVENT_INJECTION_PENDING = -1, - - /* Injection succeeded. */ - INPUT_EVENT_INJECTION_SUCCEEDED = 0, - - /* Injection failed because the injector did not have permission to inject - * into the application with input focus. */ - INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, - - /* Injection failed because there were no available input targets. */ - INPUT_EVENT_INJECTION_FAILED = 2, - - /* Injection failed due to a timeout. */ - INPUT_EVENT_INJECTION_TIMED_OUT = 3 -}; - -/* - * Constants used to determine the input event injection synchronization mode. - */ -enum { - /* Injection is asynchronous and is assumed always to be successful. */ - INPUT_EVENT_INJECTION_SYNC_NONE = 0, - - /* Waits for previous events to be dispatched so that the input dispatcher can determine - * whether input event injection willbe permitted based on the current input focus. - * Does not wait for the input event to finish processing. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, - - /* Waits for the input event to be completely processed. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, -}; - - -/* - * An input target specifies how an input event is to be dispatched to a particular window - * including the window's input channel, control flags, a timeout, and an X / Y offset to - * be added to input event coordinates to compensate for the absolute position of the - * window area. - */ -struct InputTarget { - enum { - /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - FLAG_ZERO_COORDS = 1 << 3, - - /* This flag indicates that the event should be sent as is. - * Should always be set unless the event is to be transmuted. */ - FLAG_DISPATCH_AS_IS = 1 << 8, - - /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside - * of the area of this target and so should instead be delivered as an - * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, - - /* This flag indicates that a hover sequence is starting in the given window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, - - /* This flag indicates that a hover event happened outside of a window which handled - * previous hover events, signifying the end of the current hover sequence for that - * window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, - - /* This flag indicates that the event should be canceled. - * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips - * outside of a window. */ - FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, - - /* This flag indicates that the event should be dispatched as an initial down. - * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips - * into a new window. */ - FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, - - /* Mask for all dispatch modes. */ - FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS - | FLAG_DISPATCH_AS_OUTSIDE - | FLAG_DISPATCH_AS_HOVER_ENTER - | FLAG_DISPATCH_AS_HOVER_EXIT - | FLAG_DISPATCH_AS_SLIPPERY_EXIT - | FLAG_DISPATCH_AS_SLIPPERY_ENTER, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - - }; - - // The input channel to be targeted. - sp inputChannel; - - // Flags for the input target. - int32_t flags; - - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset, yOffset; - - // Scaling factor to apply to MotionEvent as it is delivered. - // (ignored for KeyEvents) - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - - // The subset of pointer ids to include in motion events dispatched to this input target - // if FLAG_SPLIT is set. - BitSet32 pointerIds; -}; - - -/* - * Input dispatcher configuration. - * - * Specifies various options that modify the behavior of the input dispatcher. - * The values provided here are merely defaults. The actual values will come from ViewConfiguration - * and are passed into the dispatcher during initialization. - */ -struct InputDispatcherConfiguration { - // The key repeat initial timeout. - nsecs_t keyRepeatTimeout; - - // The key repeat inter-key delay. - nsecs_t keyRepeatDelay; - - InputDispatcherConfiguration() : - keyRepeatTimeout(500 * 1000000LL), - keyRepeatDelay(50 * 1000000LL) { } -}; - - -/* - * Input dispatcher policy interface. - * - * The input reader policy is used by the input reader to interact with the Window Manager - * and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class InputDispatcherPolicyInterface : public virtual RefBase { -protected: - InputDispatcherPolicyInterface() { } - virtual ~InputDispatcherPolicyInterface() { } - -public: - /* Notifies the system that a configuration change has occurred. */ - virtual void notifyConfigurationChanged(nsecs_t when) = 0; - - /* Notifies the system that an application is not responding. - * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyANR(const sp& inputApplicationHandle, - const sp& token, - const std::string& reason) = 0; - - /* Notifies the system that an input channel is unrecoverably broken. */ - virtual void notifyInputChannelBroken(const sp& token) = 0; - virtual void notifyFocusChanged(const sp& oldToken, const sp& newToken) = 0; - - /* Gets the input dispatcher configuration. */ - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; - - /* Filters an input event. - * Return true to dispatch the event unmodified, false to consume the event. - * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED - * to injectInputEvent. - */ - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; - - /* Intercepts a key event immediately before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; - - /* Intercepts a touch, trackball or other motion event before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, - uint32_t& policyFlags) = 0; - - /* Allows the policy a chance to intercept a key before dispatching. */ - virtual nsecs_t interceptKeyBeforeDispatching(const sp& token, - const KeyEvent* keyEvent, uint32_t policyFlags) = 0; - - /* Allows the policy a chance to perform default processing for an unhandled key. - * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ - virtual bool dispatchUnhandledKey(const sp& token, - const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; - - /* Notifies the policy about switch events. - */ - virtual void notifySwitch(nsecs_t when, - uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0; - - /* Poke user activity for an event dispatched to a window. */ - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; - - /* Checks whether a given application pid/uid has permission to inject input events - * into other applications. - * - * This method is special in that its implementation promises to be non-reentrant and - * is safe to call while holding other locks. (Most other methods make no such guarantees!) - */ - virtual bool checkInjectEventsPermissionNonReentrant( - int32_t injectorPid, int32_t injectorUid) = 0; - - /* Notifies the policy that a pointer down event has occurred outside the current focused - * window. - * - * The touchedToken passed as an argument is the window that received the input event. - */ - virtual void onPointerDownOutsideFocus(const sp& touchedToken) = 0; -}; - - -/* Notifies the system about input events generated by the input reader. - * The dispatcher is expected to be mostly asynchronous. */ -class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { -protected: - InputDispatcherInterface() { } - virtual ~InputDispatcherInterface() { } - -public: - /* Dumps the state of the input dispatcher. - * - * This method may be called on any thread (usually by the input manager). */ - virtual void dump(std::string& dump) = 0; - - /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ - virtual void monitor() = 0; - - /* Runs a single iteration of the dispatch loop. - * Nominally processes one queued event, a timeout, or a response from an input consumer. - * - * This method should only be called on the input dispatcher thread. - */ - virtual void dispatchOnce() = 0; - - /* Injects an input event and optionally waits for sync. - * The synchronization mode determines whether the method blocks while waiting for - * input injection to proceed. - * Returns one of the INPUT_EVENT_INJECTION_XXX constants. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) = 0; - - /* Sets the list of input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputWindows(const std::vector >& inputWindowHandles, - int32_t displayId, - const sp& setInputWindowsListener = nullptr) = 0; - - /* Sets the focused application on the given display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedApplication( - int32_t displayId, const sp& inputApplicationHandle) = 0; - - /* Sets the focused display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedDisplay(int32_t displayId) = 0; - - /* Sets the input dispatching mode. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; - - /* Sets whether input event filtering is enabled. - * When enabled, incoming input events are sent to the policy's filterInputEvent - * method instead of being dispatched. The filter is expected to use - * injectInputEvent to inject the events it would like to have dispatched. - * It should include POLICY_FLAG_FILTERED in the policy flags during injection. - */ - virtual void setInputFilterEnabled(bool enabled) = 0; - - /* Transfers touch focus from one window to another window. - * - * Returns true on success. False if the window did not actually have touch focus. - */ - virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; - - /* Registers input channels that may be used as targets for input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputChannel( - const sp& inputChannel, int32_t displayId) = 0; - - /* Registers input channels to be used to monitor input events. - * - * Each monitor must target a specific display and will only receive input events sent to that - * display. If the monitor is a gesture monitor, it will only receive pointer events on the - * targeted display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputMonitor( - const sp& inputChannel, int32_t displayId, bool gestureMonitor) = 0; - - /* Unregister input channels that will no longer receive input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; - - /* Allows an input monitor steal the current pointer stream away from normal input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t pilferPointers(const sp& token) = 0; - -}; - -/* Dispatches events to input targets. Some functions of the input dispatcher, such as - * identifying input targets, are controlled by a separate policy object. - * - * IMPORTANT INVARIANT: - * Because the policy can potentially block or cause re-entrance into the input dispatcher, - * the input dispatcher never calls into the policy while holding its internal locks. - * The implementation is also carefully designed to recover from scenarios such as an - * input channel becoming unregistered while identifying input targets or processing timeouts. - * - * Methods marked 'Locked' must be called with the lock acquired. - * - * Methods marked 'LockedInterruptible' must be called with the lock acquired but - * may during the course of their execution release the lock, call into the policy, and - * then reacquire the lock. The caller is responsible for recovering gracefully. - * - * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. - */ -class InputDispatcher : public InputDispatcherInterface { -protected: - virtual ~InputDispatcher(); - -public: - explicit InputDispatcher(const sp& policy); - - virtual void dump(std::string& dump) override; - virtual void monitor() override; - - virtual void dispatchOnce() override; - - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; - virtual void notifyKey(const NotifyKeyArgs* args) override; - virtual void notifyMotion(const NotifyMotionArgs* args) override; - virtual void notifySwitch(const NotifySwitchArgs* args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) override; - - virtual void setInputWindows(const std::vector >& inputWindowHandles, - int32_t displayId, - const sp& setInputWindowsListener = nullptr) override; - virtual void setFocusedApplication(int32_t displayId, - const sp& inputApplicationHandle) override; - virtual void setFocusedDisplay(int32_t displayId) override; - virtual void setInputDispatchMode(bool enabled, bool frozen) override; - virtual void setInputFilterEnabled(bool enabled) override; - - virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) - override; - - virtual status_t registerInputChannel(const sp& inputChannel, - int32_t displayId) override; - virtual status_t registerInputMonitor(const sp& inputChannel, - int32_t displayId, bool isGestureMonitor) override; - virtual status_t unregisterInputChannel(const sp& inputChannel) override; - virtual status_t pilferPointers(const sp& token) override; - -private: - - struct InjectionState { - mutable int32_t refCount; - - int32_t injectorPid; - int32_t injectorUid; - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING - bool injectionIsAsync; // set to true if injection is not waiting for the result - int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress - - InjectionState(int32_t injectorPid, int32_t injectorUid); - void release(); - - private: - ~InjectionState(); - }; - - struct EventEntry { - enum { - TYPE_CONFIGURATION_CHANGED, - TYPE_DEVICE_RESET, - TYPE_KEY, - TYPE_MOTION - }; - - uint32_t sequenceNum; - mutable int32_t refCount; - int32_t type; - nsecs_t eventTime; - uint32_t policyFlags; - InjectionState* injectionState; - - bool dispatchInProgress; // initially false, set to true while dispatching - - inline bool isInjected() const { return injectionState != nullptr; } - - void release(); - - virtual void appendDescription(std::string& msg) const = 0; - - protected: - EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); - virtual ~EventEntry(); - void releaseInjectionState(); - }; - - struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~ConfigurationChangedEntry(); - }; - - struct DeviceResetEntry : EventEntry { - int32_t deviceId; - - DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~DeviceResetEntry(); - }; - - struct KeyEntry : EventEntry { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t repeatCount; - nsecs_t downTime; - - bool syntheticRepeat; // set to true for synthetic key repeats - - enum InterceptKeyResult { - INTERCEPT_KEY_RESULT_UNKNOWN, - INTERCEPT_KEY_RESULT_SKIP, - INTERCEPT_KEY_RESULT_CONTINUE, - INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, - }; - InterceptKeyResult interceptKeyResult; // set based on the interception result - nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER - - KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - int32_t repeatCount, nsecs_t downTime); - virtual void appendDescription(std::string& msg) const; - void recycle(); - - protected: - virtual ~KeyEntry(); - }; - - struct MotionEntry : EventEntry { - nsecs_t eventTime; - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t actionButton; - int32_t flags; - int32_t metaState; - int32_t buttonState; - MotionClassification classification; - int32_t edgeFlags; - float xPrecision; - float yPrecision; - float xCursorPosition; - float yCursorPosition; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~MotionEntry(); - }; - - // Tracks the progress of dispatching a particular event to a particular connection. - struct DispatchEntry { - const uint32_t seq; // unique sequence number, never 0 - - EventEntry* eventEntry; // the event to dispatch - int32_t targetFlags; - float xOffset; - float yOffset; - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - nsecs_t deliveryTime; // time when the event was actually delivered - - // Set to the resolved action and flags when the event is enqueued. - int32_t resolvedAction; - int32_t resolvedFlags; - - DispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, - float globalScaleFactor, float windowXScale, float windowYScale); - ~DispatchEntry(); - - inline bool hasForegroundTarget() const { - return targetFlags & InputTarget::FLAG_FOREGROUND; - } - - inline bool isSplit() const { - return targetFlags & InputTarget::FLAG_SPLIT; - } - - private: - static volatile int32_t sNextSeqAtomic; - - static uint32_t nextSeq(); - }; - - // A command entry captures state and behavior for an action to be performed in the - // dispatch loop after the initial processing has taken place. It is essentially - // a kind of continuation used to postpone sensitive policy interactions to a point - // in the dispatch loop where it is safe to release the lock (generally after finishing - // the critical parts of the dispatch cycle). - // - // The special thing about commands is that they can voluntarily release and reacquire - // the dispatcher lock at will. Initially when the command starts running, the - // dispatcher lock is held. However, if the command needs to call into the policy to - // do some work, it can release the lock, do the work, then reacquire the lock again - // before returning. - // - // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch - // never calls into the policy while holding its lock. - // - // Commands are implicitly 'LockedInterruptible'. - struct CommandEntry; - typedef std::function Command; - - class Connection; - struct CommandEntry { - explicit CommandEntry(Command command); - ~CommandEntry(); - - Command command; - - // parameters for the command (usage varies by command) - sp connection; - nsecs_t eventTime; - KeyEntry* keyEntry; - sp inputApplicationHandle; - std::string reason; - int32_t userActivityEventType; - uint32_t seq; - bool handled; - sp inputChannel; - sp oldToken; - sp newToken; - }; - - /* Specifies which events are to be canceled and why. */ - struct CancelationOptions { - enum Mode { - CANCEL_ALL_EVENTS = 0, - CANCEL_POINTER_EVENTS = 1, - CANCEL_NON_POINTER_EVENTS = 2, - CANCEL_FALLBACK_EVENTS = 3, - }; - - // The criterion to use to determine which events should be canceled. - Mode mode; - - // Descriptive reason for the cancelation. - const char* reason; - - // The specific keycode of the key event to cancel, or nullopt to cancel any key event. - std::optional keyCode = std::nullopt; - - // The specific device id of events to cancel, or nullopt to cancel events from any device. - std::optional deviceId = std::nullopt; - - // The specific display id of events to cancel, or nullopt to cancel events on any display. - std::optional displayId = std::nullopt; - - CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { } - }; - - /* Tracks dispatched key and motion event state so that cancelation events can be - * synthesized when events are dropped. */ - class InputState { - public: - InputState(); - ~InputState(); - - // Returns true if there is no state to be canceled. - bool isNeutral() const; - - // Returns true if the specified source is known to have received a hover enter - // motion event. - bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; - - // Records tracking information for a key event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); - - // Records tracking information for a motion event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); - - // Synthesizes cancelation events for the current state and resets the tracked state. - void synthesizeCancelationEvents(nsecs_t currentTime, - std::vector& outEvents, const CancelationOptions& options); - - // Clears the current state. - void clear(); - - // Copies pointer-related parts of the input state to another instance. - void copyPointerStateTo(InputState& other) const; - - // Gets the fallback key associated with a keycode. - // Returns -1 if none. - // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. - int32_t getFallbackKey(int32_t originalKeyCode); - - // Sets the fallback key for a particular keycode. - void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); - - // Removes the fallback key for a particular keycode. - void removeFallbackKey(int32_t originalKeyCode); - - inline const KeyedVector& getFallbackKeys() const { - return mFallbackKeys; - } - - private: - struct KeyMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t flags; - nsecs_t downTime; - uint32_t policyFlags; - }; - - struct MotionMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t flags; - float xPrecision; - float yPrecision; - float xCursorPosition; - float yCursorPosition; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - bool hovering; - uint32_t policyFlags; - - void setPointers(const MotionEntry* entry); - }; - - std::vector mKeyMementos; - std::vector mMotionMementos; - KeyedVector mFallbackKeys; - - ssize_t findKeyMemento(const KeyEntry* entry) const; - ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; - - void addKeyMemento(const KeyEntry* entry, int32_t flags); - void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); - - static bool shouldCancelKey(const KeyMemento& memento, - const CancelationOptions& options); - static bool shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options); - }; - - /* Manages the dispatch state associated with a single input channel. */ - class Connection : public RefBase { - protected: - virtual ~Connection(); - - public: - enum Status { - // Everything is peachy. - STATUS_NORMAL, - // An unrecoverable communication error has occurred. - STATUS_BROKEN, - // The input channel has been unregistered. - STATUS_ZOMBIE - }; - - Status status; - sp inputChannel; // never null - bool monitor; - InputPublisher inputPublisher; - InputState inputState; - - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; - - // Queue of events that need to be published to the connection. - std::deque outboundQueue; - - // Queue of events that have been published to the connection but that have not - // yet received a "finished" response from the application. - std::deque waitQueue; - - explicit Connection(const sp& inputChannel, bool monitor); - - inline const std::string getInputChannelName() const { return inputChannel->getName(); } - - const std::string getWindowName() const; - const char* getStatusLabel() const; - - std::deque::iterator findWaitQueueEntry(uint32_t seq); - }; - - struct Monitor { - sp inputChannel; // never null - - explicit Monitor(const sp& inputChannel); - }; - - enum DropReason { - DROP_REASON_NOT_DROPPED = 0, - DROP_REASON_POLICY = 1, - DROP_REASON_APP_SWITCH = 2, - DROP_REASON_DISABLED = 3, - DROP_REASON_BLOCKED = 4, - DROP_REASON_STALE = 5, - }; - - sp mPolicy; - InputDispatcherConfiguration mConfig; - - std::mutex mLock; - - std::condition_variable mDispatcherIsAlive; - - sp mLooper; - - EventEntry* mPendingEvent GUARDED_BY(mLock); - std::deque mInboundQueue GUARDED_BY(mLock); - std::deque mRecentQueue GUARDED_BY(mLock); - std::deque> mCommandQueue GUARDED_BY(mLock); - - DropReason mLastDropReason GUARDED_BY(mLock); - - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); - - // Enqueues an inbound event. Returns true if mLooper->wake() should be called. - bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); - - // Cleans up input state when dropping an inbound event. - void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); - - // Adds an event to a queue of recent events for debugging purposes. - void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); - - // App switch latency optimization. - bool mAppSwitchSawKeyDown GUARDED_BY(mLock); - nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); - - bool isAppSwitchKeyEvent(KeyEntry* keyEntry); - bool isAppSwitchPendingLocked() REQUIRES(mLock); - void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); - - // Stale event latency optimization. - static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); - - // Blocked event latency optimization. Drops old events when the user intends - // to transfer focus to a new application. - EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); - - sp findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, - bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock); - - // All registered connections mapped by channel file descriptor. - std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); - - struct IBinderHash { - std::size_t operator()(const sp& b) const { - return std::hash{}(b.get()); - } - }; - std::unordered_map, sp, IBinderHash> mInputChannelsByToken - GUARDED_BY(mLock); - - // Finds the display ID of the gesture monitor identified by the provided token. - std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) - REQUIRES(mLock); - - sp getConnectionLocked(const sp& inputChannel) REQUIRES(mLock); - - // Input channels that will receive a copy of all input events sent to the provided display. - std::unordered_map> mGlobalMonitorsByDisplay - GUARDED_BY(mLock); - - // Input channels that will receive pointer events that start within the corresponding display. - // These are a bit special when compared to global monitors since they'll cause gesture streams - // to continue even when there isn't a touched window,and have the ability to steal the rest of - // the pointer stream in order to claim it for a system gesture. - std::unordered_map> mGestureMonitorsByDisplay - GUARDED_BY(mLock); - - - // Event injection and synchronization. - std::condition_variable mInjectionResultAvailable; - bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); - void setInjectionResult(EventEntry* entry, int32_t injectionResult); - - std::condition_variable mInjectionSyncFinished; - void incrementPendingForegroundDispatches(EventEntry* entry); - void decrementPendingForegroundDispatches(EventEntry* entry); - - // Key repeat tracking. - struct KeyRepeatState { - KeyEntry* lastKeyEntry; // or null if no repeat - nsecs_t nextRepeatTime; - } mKeyRepeatState GUARDED_BY(mLock); - - void resetKeyRepeatLocked() REQUIRES(mLock); - KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); - - // Key replacement tracking - struct KeyReplacement { - int32_t keyCode; - int32_t deviceId; - bool operator==(const KeyReplacement& rhs) const { - return keyCode == rhs.keyCode && deviceId == rhs.deviceId; - } - bool operator<(const KeyReplacement& rhs) const { - return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId; - } - }; - // Maps the key code replaced, device id tuple to the key code it was replaced with - KeyedVector mReplacedKeys GUARDED_BY(mLock); - // Process certain Meta + Key combinations - void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, - int32_t& keyCode, int32_t& metaState); - - // Deferred command processing. - bool haveCommandsLocked() const REQUIRES(mLock); - bool runCommandsLockedInterruptible() REQUIRES(mLock); - void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); - - // Input filter processing. - bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); - bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); - - // Inbound event processing. - void drainInboundQueueLocked() REQUIRES(mLock); - void releasePendingEventLocked() REQUIRES(mLock); - void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); - - // Dispatch state. - bool mDispatchEnabled GUARDED_BY(mLock); - bool mDispatchFrozen GUARDED_BY(mLock); - bool mInputFilterEnabled GUARDED_BY(mLock); - - std::unordered_map>> mWindowHandlesByDisplay - GUARDED_BY(mLock); - // Get window handles by display, return an empty vector if not found. - std::vector> getWindowHandlesLocked(int32_t displayId) const - REQUIRES(mLock); - sp getWindowHandleLocked(const sp& windowHandleToken) const - REQUIRES(mLock); - sp getInputChannelLocked(const sp& windowToken) const REQUIRES(mLock); - bool hasWindowHandleLocked(const sp& windowHandle) const REQUIRES(mLock); - - /* - * Validate and update InputWindowHandles for a given display. - */ - void updateWindowHandlesForDisplayLocked( - const std::vector>& inputWindowHandles, int32_t displayId) - REQUIRES(mLock); - - // Focus tracking for keys, trackball, etc. - std::unordered_map> mFocusedWindowHandlesByDisplay - GUARDED_BY(mLock); - - // Focus tracking for touch. - struct TouchedWindow { - sp windowHandle; - int32_t targetFlags; - BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set - }; - - // For tracking the offsets we need to apply when adding gesture monitor targets. - struct TouchedMonitor { - Monitor monitor; - float xOffset = 0.f; - float yOffset = 0.f; - - explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); - }; - - struct TouchState { - bool down; - bool split; - int32_t deviceId; // id of the device that is currently down, others are rejected - uint32_t source; // source of the device that is current down, others are rejected - int32_t displayId; // id to the display that currently has a touch, others are rejected - std::vector windows; - - // This collects the portal windows that the touch has gone through. Each portal window - // targets a display (embedded display for most cases). With this info, we can add the - // monitoring channels of the displays touched. - std::vector> portalWindows; - - std::vector gestureMonitors; - - TouchState(); - ~TouchState(); - void reset(); - void copyFrom(const TouchState& other); - void addOrUpdateWindow(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds); - void addPortalWindow(const sp& windowHandle); - void addGestureMonitors(const std::vector& monitors); - void removeWindow(const sp& windowHandle); - void removeWindowByToken(const sp& token); - void filterNonAsIsTouchWindows(); - void filterNonMonitors(); - sp getFirstForegroundWindowHandle() const; - bool isSlippery() const; - }; - - KeyedVector mTouchStatesByDisplay GUARDED_BY(mLock); - TouchState mTempTouchState GUARDED_BY(mLock); - - // Focused applications. - std::unordered_map> mFocusedApplicationHandlesByDisplay - GUARDED_BY(mLock); - - // Top focused display. - int32_t mFocusedDisplayId GUARDED_BY(mLock); - - // Dispatcher state at time of last ANR. - std::string mLastANRState GUARDED_BY(mLock); - - // Dispatch inbound events. - bool dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock); - bool dispatchDeviceResetLocked( - nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); - bool dispatchKeyLocked( - nsecs_t currentTime, KeyEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); - bool dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); - void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, - const std::vector& inputTargets) REQUIRES(mLock); - - void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); - void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); - - // Keeping track of ANR timeouts. - enum InputTargetWaitCause { - INPUT_TARGET_WAIT_CAUSE_NONE, - INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, - INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, - }; - - InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); - nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); - nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); - bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); - sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); - - // Contains the last window which received a hover event. - sp mLastHoverWindowHandle GUARDED_BY(mLock); - - // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, - const sp& applicationHandle, - const sp& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock); - - void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); - - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputChannel) REQUIRES(mLock); - nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetANRTimeoutsLocked() REQUIRES(mLock); - - int32_t getTargetDisplayId(const EventEntry* entry); - int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, - std::vector& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock); - int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, - std::vector& inputTargets, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) REQUIRES(mLock); - std::vector findTouchedGestureMonitorsLocked(int32_t displayId, - const std::vector>& portalWindows) REQUIRES(mLock); - void addGestureMonitors(const std::vector& monitors, - std::vector& outTouchedMonitors, float xOffset = 0, float yOffset = 0); - - void addWindowTargetLocked(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) - REQUIRES(mLock); - void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, - std::vector& inputTargets) REQUIRES(mLock); - void addGlobalMonitoringTargetsLocked(std::vector& inputTargets, - int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); - - void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); - bool checkInjectionPermission(const sp& windowHandle, - const InjectionState* injectionState); - bool isWindowObscuredAtPointLocked(const sp& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); - bool isWindowObscuredLocked(const sp& windowHandle) const REQUIRES(mLock); - std::string getApplicationWindowLabel(const sp& applicationHandle, - const sp& windowHandle); - - std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp& windowHandle, const EventEntry* eventEntry, - const char* targetType) REQUIRES(mLock); - - // Manage the dispatch cycle for a single connection. - // These methods are deliberately not Interruptible because doing all of the work - // with the mutex held makes it easier to ensure that connection invariants are maintained. - // If needed, the methods post commands to run later once the critical bits are done. - void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); - void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); - void enqueueDispatchEntryLocked(const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) - REQUIRES(mLock); - void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection) - REQUIRES(mLock); - void finishDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - uint32_t seq, bool handled) REQUIRES(mLock); - void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - bool notify) REQUIRES(mLock); - void drainDispatchQueue(std::deque& queue); - void releaseDispatchEntry(DispatchEntry* dispatchEntry); - static int handleReceiveCallback(int fd, int events, void* data); - // The action sent should only be of type AMOTION_EVENT_* - void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, - const sp& newToken) REQUIRES(mLock); - - void synthesizeCancelationEventsForAllConnectionsLocked( - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForMonitorsLocked( - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options, - std::unordered_map>& monitorsByDisplay) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked(const sp& channel, - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForConnectionLocked(const sp& connection, - const CancelationOptions& options) REQUIRES(mLock); - - // Splitting motion events across windows. - MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); - - // Reset and drop everything the dispatcher is doing. - void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); - - // Dump state. - void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); - void dumpMonitors(std::string& dump, const std::vector& monitors); - void logDispatchStateLocked() REQUIRES(mLock); - - // Registration. - void removeMonitorChannelLocked(const sp& inputChannel) REQUIRES(mLock); - void removeMonitorChannelLocked(const sp& inputChannel, - std::unordered_map>& monitorsByDisplay) - REQUIRES(mLock); - status_t unregisterInputChannelLocked(const sp& inputChannel, bool notify) - REQUIRES(mLock); - - // Interesting events that we might like to log or tell the framework about. - void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) - REQUIRES(mLock); - void onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp& connection) REQUIRES(mLock); - void onFocusChangedLocked(const sp& oldFocus, - const sp& newFocus) REQUIRES(mLock); - void onANRLocked( - nsecs_t currentTime, const sp& applicationHandle, - const sp& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); - - // Outbound policy interactions. - void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - bool afterKeyEventLockedInterruptible(const sp& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock); - bool afterMotionEventLockedInterruptible(const sp& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock); - void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); - void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - - // Statistics gathering. - void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); - void traceInboundQueueLengthLocked() REQUIRES(mLock); - void traceOutboundQueueLength(const sp& connection); - void traceWaitQueueLength(const sp& connection); - - sp mReporter; -}; - -/* Enqueues and dispatches input events, endlessly. */ -class InputDispatcherThread : public Thread { -public: - explicit InputDispatcherThread(const sp& dispatcher); - ~InputDispatcherThread(); - -private: - virtual bool threadLoop(); - - sp mDispatcher; -}; - -} // namespace android - -#endif // _UI_INPUT_DISPATCHER_H diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp new file mode 100644 index 0000000000..4b8c51be5e --- /dev/null +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -0,0 +1,5359 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputDispatcher" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#define LOG_NDEBUG 0 + +// Log detailed debug messages about each inbound event notification to the dispatcher. +#define DEBUG_INBOUND_EVENT_DETAILS 0 + +// Log detailed debug messages about each outbound event processed by the dispatcher. +#define DEBUG_OUTBOUND_EVENT_DETAILS 0 + +// Log debug messages about the dispatch cycle. +#define DEBUG_DISPATCH_CYCLE 0 + +// Log debug messages about registrations. +#define DEBUG_REGISTRATION 0 + +// Log debug messages about input event injection. +#define DEBUG_INJECTION 0 + +// Log debug messages about input focus tracking. +#define DEBUG_FOCUS 0 + +// Log debug messages about the app switch latency optimization. +#define DEBUG_APP_SWITCH 0 + +// Log debug messages about hover events. +#define DEBUG_HOVER 0 + +#include "InputDispatcher.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define INDENT " " +#define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " + +using android::base::StringPrintf; + +namespace android { + +// Default input dispatching timeout if there is no focused application or paused window +// from which to determine an appropriate dispatching timeout. +constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec + +// Amount of time to allow for all pending events to be processed when an app switch +// key is on the way. This is used to preempt input dispatch and drop input events +// when an application takes too long to respond and the user has pressed an app switch key. +constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec + +// Amount of time to allow for an event to be dispatched (measured since its eventTime) +// before considering it stale and dropping it. +constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec + +// Amount of time to allow touch events to be streamed out to a connection before requiring +// that the first event be finished. This value extends the ANR timeout by the specified +// amount. For example, if streaming is allowed to get ahead by one second relative to the +// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. +constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec + +// Log a warning when an event takes longer than this to process, even if an ANR does not occur. +constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec + +// Log a warning when an interception call takes longer than this to process. +constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; + +// Number of recent events to keep for debugging purposes. +constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; + +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; + +static inline nsecs_t now() { + return systemTime(SYSTEM_TIME_MONOTONIC); +} + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +static std::string motionActionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return StringPrintf("%" PRId32, action); +} + +static std::string keyActionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return StringPrintf("%" PRId32, action); +} + +static std::string dispatchModeToString(int32_t dispatchMode) { + switch (dispatchMode) { + case InputTarget::FLAG_DISPATCH_AS_IS: + return "DISPATCH_AS_IS"; + case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: + return "DISPATCH_AS_OUTSIDE"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: + return "DISPATCH_AS_HOVER_ENTER"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: + return "DISPATCH_AS_HOVER_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: + return "DISPATCH_AS_SLIPPERY_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: + return "DISPATCH_AS_SLIPPERY_ENTER"; + } + return StringPrintf("%" PRId32, dispatchMode); +} + +static inline int32_t getMotionEventActionPointerIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; +} + +static bool isValidKeyAction(int32_t action) { + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return true; + default: + return false; + } +} + +static bool validateKeyEvent(int32_t action) { + if (!isValidKeyAction(action)) { + ALOGE("Key event has invalid action code 0x%x", action); + return false; + } + return true; +} + +static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_OUTSIDE: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_HOVER_EXIT: + case AMOTION_EVENT_ACTION_SCROLL: + return true; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: { + int32_t index = getMotionEventActionPointerIndex(action); + return index >= 0 && index < pointerCount; + } + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: + return actionButton != 0; + default: + return false; + } +} + +static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, + const PointerProperties* pointerProperties) { + if (!isValidMotionAction(action, actionButton, pointerCount)) { + ALOGE("Motion event has invalid action code 0x%x", action); + return false; + } + if (pointerCount < 1 || pointerCount > MAX_POINTERS) { + ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.", + pointerCount, MAX_POINTERS); + return false; + } + BitSet32 pointerIdBits; + for (size_t i = 0; i < pointerCount; i++) { + int32_t id = pointerProperties[i].id; + if (id < 0 || id > MAX_POINTER_ID) { + ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id, + MAX_POINTER_ID); + return false; + } + if (pointerIdBits.hasBit(id)) { + ALOGE("Motion event has duplicate pointer id %d", id); + return false; + } + pointerIdBits.markBit(id); + } + return true; +} + +static void dumpRegion(std::string& dump, const Region& region) { + if (region.isEmpty()) { + dump += ""; + return; + } + + bool first = true; + Region::const_iterator cur = region.begin(); + Region::const_iterator const tail = region.end(); + while (cur != tail) { + if (first) { + first = false; + } else { + dump += "|"; + } + dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom); + cur++; + } +} + +/** + * Find the entry in std::unordered_map by key, and return it. + * If the entry is not found, return a default constructed entry. + * + * Useful when the entries are vectors, since an empty vector will be returned + * if the entry is not found. + * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. + */ +template +static V getValueByKey(const std::unordered_map& map, K key) { + auto it = map.find(key); + return it != map.end() ? it->second : V{}; +} + +/** + * Find the entry in std::unordered_map by value, and remove it. + * If more than one entry has the same value, then all matching + * key-value pairs will be removed. + * + * Return true if at least one value has been removed. + */ +template +static bool removeByValue(std::unordered_map& map, const V& value) { + bool removed = false; + for (auto it = map.begin(); it != map.end();) { + if (it->second == value) { + it = map.erase(it); + removed = true; + } else { + it++; + } + } + return removed; +} + +// --- InputDispatcher --- + +InputDispatcher::InputDispatcher(const sp& policy) + : mPolicy(policy), + mPendingEvent(nullptr), + mLastDropReason(DROP_REASON_NOT_DROPPED), + mAppSwitchSawKeyDown(false), + mAppSwitchDueTime(LONG_LONG_MAX), + mNextUnblockedEvent(nullptr), + mDispatchEnabled(false), + mDispatchFrozen(false), + mInputFilterEnabled(false), + mFocusedDisplayId(ADISPLAY_ID_DEFAULT), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { + mLooper = new Looper(false); + mReporter = createInputReporter(); + + mKeyRepeatState.lastKeyEntry = nullptr; + + policy->getDispatcherConfiguration(&mConfig); +} + +InputDispatcher::~InputDispatcher() { + { // acquire lock + std::scoped_lock _l(mLock); + + resetKeyRepeatLocked(); + releasePendingEventLocked(); + drainInboundQueueLocked(); + } + + while (!mConnectionsByFd.empty()) { + sp connection = mConnectionsByFd.begin()->second; + unregisterInputChannel(connection->inputChannel); + } +} + +void InputDispatcher::dispatchOnce() { + nsecs_t nextWakeupTime = LONG_LONG_MAX; + { // acquire lock + std::scoped_lock _l(mLock); + mDispatcherIsAlive.notify_all(); + + // Run a dispatch loop if there are no pending commands. + // The dispatch loop might enqueue commands to run afterwards. + if (!haveCommandsLocked()) { + dispatchOnceInnerLocked(&nextWakeupTime); + } + + // Run all pending commands if there are any. + // If any commands were run then force the next poll to wake up immediately. + if (runCommandsLockedInterruptible()) { + nextWakeupTime = LONG_LONG_MIN; + } + } // release lock + + // Wait for callback or timeout or wake. (make sure we round up, not down) + nsecs_t currentTime = now(); + int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); + mLooper->pollOnce(timeoutMillis); +} + +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { + nsecs_t currentTime = now(); + + // Reset the key repeat timer whenever normal dispatch is suspended while the + // device is in a non-interactive state. This is to ensure that we abort a key + // repeat if the device is just coming out of sleep. + if (!mDispatchEnabled) { + resetKeyRepeatLocked(); + } + + // If dispatching is frozen, do not process timeouts or try to deliver any new events. + if (mDispatchFrozen) { +#if DEBUG_FOCUS + ALOGD("Dispatch frozen. Waiting some more."); +#endif + return; + } + + // Optimize latency of app switches. + // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has + // been pressed. When it expires, we preempt dispatch and drop all other pending events. + bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; + if (mAppSwitchDueTime < *nextWakeupTime) { + *nextWakeupTime = mAppSwitchDueTime; + } + + // Ready to start a new event. + // If we don't already have a pending event, go grab one. + if (!mPendingEvent) { + if (mInboundQueue.empty()) { + if (isAppSwitchDue) { + // The inbound queue is empty so the app switch key we were waiting + // for will never arrive. Stop waiting for it. + resetPendingAppSwitchLocked(false); + isAppSwitchDue = false; + } + + // Synthesize a key repeat if appropriate. + if (mKeyRepeatState.lastKeyEntry) { + if (currentTime >= mKeyRepeatState.nextRepeatTime) { + mPendingEvent = synthesizeKeyRepeatLocked(currentTime); + } else { + if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { + *nextWakeupTime = mKeyRepeatState.nextRepeatTime; + } + } + } + + // Nothing to do if there is no pending event. + if (!mPendingEvent) { + return; + } + } else { + // Inbound queue has at least one entry. + mPendingEvent = mInboundQueue.front(); + mInboundQueue.pop_front(); + traceInboundQueueLengthLocked(); + } + + // Poke user activity for this event. + if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { + pokeUserActivityLocked(mPendingEvent); + } + + // Get ready to dispatch the event. + resetANRTimeoutsLocked(); + } + + // Now we have an event to dispatch. + // All events are eventually dequeued and processed this way, even if we intend to drop them. + ALOG_ASSERT(mPendingEvent != nullptr); + bool done = false; + DropReason dropReason = DROP_REASON_NOT_DROPPED; + if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { + dropReason = DROP_REASON_POLICY; + } else if (!mDispatchEnabled) { + dropReason = DROP_REASON_DISABLED; + } + + if (mNextUnblockedEvent == mPendingEvent) { + mNextUnblockedEvent = nullptr; + } + + switch (mPendingEvent->type) { + case EventEntry::TYPE_CONFIGURATION_CHANGED: { + ConfigurationChangedEntry* typedEntry = + static_cast(mPendingEvent); + done = dispatchConfigurationChangedLocked(currentTime, typedEntry); + dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped + break; + } + + case EventEntry::TYPE_DEVICE_RESET: { + DeviceResetEntry* typedEntry = static_cast(mPendingEvent); + done = dispatchDeviceResetLocked(currentTime, typedEntry); + dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped + break; + } + + case EventEntry::TYPE_KEY: { + KeyEntry* typedEntry = static_cast(mPendingEvent); + if (isAppSwitchDue) { + if (isAppSwitchKeyEvent(typedEntry)) { + resetPendingAppSwitchLocked(true); + isAppSwitchDue = false; + } else if (dropReason == DROP_REASON_NOT_DROPPED) { + dropReason = DROP_REASON_APP_SWITCH; + } + } + if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DROP_REASON_STALE; + } + if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DROP_REASON_BLOCKED; + } + done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* typedEntry = static_cast(mPendingEvent); + if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { + dropReason = DROP_REASON_APP_SWITCH; + } + if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DROP_REASON_STALE; + } + if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DROP_REASON_BLOCKED; + } + done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + break; + } + + default: + ALOG_ASSERT(false); + break; + } + + if (done) { + if (dropReason != DROP_REASON_NOT_DROPPED) { + dropInboundEventLocked(mPendingEvent, dropReason); + } + mLastDropReason = dropReason; + + releasePendingEventLocked(); + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + } +} + +bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { + bool needWake = mInboundQueue.empty(); + mInboundQueue.push_back(entry); + traceInboundQueueLengthLocked(); + + switch (entry->type) { + case EventEntry::TYPE_KEY: { + // Optimize app switch latency. + // If the application takes too long to catch up then we drop all events preceding + // the app switch key. + KeyEntry* keyEntry = static_cast(entry); + if (isAppSwitchKeyEvent(keyEntry)) { + if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { + mAppSwitchSawKeyDown = true; + } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + if (mAppSwitchSawKeyDown) { +#if DEBUG_APP_SWITCH + ALOGD("App switch is pending!"); +#endif + mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; + mAppSwitchSawKeyDown = false; + needWake = true; + } + } + } + break; + } + + case EventEntry::TYPE_MOTION: { + // Optimize case where the current application is unresponsive and the user + // decides to touch a window in a different application. + // If the application takes too long to catch up then we drop all events preceding + // the touch into the other window. + MotionEntry* motionEntry = static_cast(entry); + if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && + (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && + mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && + mInputTargetWaitApplicationToken != nullptr) { + int32_t displayId = motionEntry->displayId; + int32_t x = + int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = + int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + sp touchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y); + if (touchedWindowHandle != nullptr && + touchedWindowHandle->getApplicationToken() != + mInputTargetWaitApplicationToken) { + // User touched a different application than the one we are waiting on. + // Flag the event, and start pruning the input queue. + mNextUnblockedEvent = motionEntry; + needWake = true; + } + } + break; + } + } + + return needWake; +} + +void InputDispatcher::addRecentEventLocked(EventEntry* entry) { + entry->refCount += 1; + mRecentQueue.push_back(entry); + if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { + mRecentQueue.front()->release(); + mRecentQueue.pop_front(); + } +} + +sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, + int32_t y, bool addOutsideTargets, + bool addPortalWindows) { + // Traverse windows from front to back to find touched window. + const std::vector> windowHandles = getWindowHandlesLocked(displayId); + for (const sp& windowHandle : windowHandles) { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + if (windowInfo->displayId == displayId) { + int32_t flags = windowInfo->layoutParamsFlags; + + if (windowInfo->visible) { + if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { + bool isTouchModal = (flags & + (InputWindowInfo::FLAG_NOT_FOCUSABLE | + InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { + int32_t portalToDisplayId = windowInfo->portalToDisplayId; + if (portalToDisplayId != ADISPLAY_ID_NONE && + portalToDisplayId != displayId) { + if (addPortalWindows) { + // For the monitoring channels of the display. + mTempTouchState.addPortalWindow(windowHandle); + } + return findTouchedWindowAtLocked(portalToDisplayId, x, y, + addOutsideTargets, addPortalWindows); + } + // Found window. + return windowHandle; + } + } + + if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) { + mTempTouchState.addOrUpdateWindow(windowHandle, + InputTarget::FLAG_DISPATCH_AS_OUTSIDE, + BitSet32(0)); + } + } + } + } + return nullptr; +} + +std::vector InputDispatcher::findTouchedGestureMonitorsLocked( + int32_t displayId, const std::vector>& portalWindows) { + std::vector touchedMonitors; + + std::vector monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); + addGestureMonitors(monitors, touchedMonitors); + for (const sp& portalWindow : portalWindows) { + const InputWindowInfo* windowInfo = portalWindow->getInfo(); + monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); + addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft, + -windowInfo->frameTop); + } + return touchedMonitors; +} + +void InputDispatcher::addGestureMonitors(const std::vector& monitors, + std::vector& outTouchedMonitors, + float xOffset, float yOffset) { + if (monitors.empty()) { + return; + } + outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size()); + for (const Monitor& monitor : monitors) { + outTouchedMonitors.emplace_back(monitor, xOffset, yOffset); + } +} + +void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { + const char* reason; + switch (dropReason) { + case DROP_REASON_POLICY: +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("Dropped event because policy consumed it."); +#endif + reason = "inbound event was dropped because the policy consumed it"; + break; + case DROP_REASON_DISABLED: + if (mLastDropReason != DROP_REASON_DISABLED) { + ALOGI("Dropped event because input dispatch is disabled."); + } + reason = "inbound event was dropped because input dispatch is disabled"; + break; + case DROP_REASON_APP_SWITCH: + ALOGI("Dropped event because of pending overdue app switch."); + reason = "inbound event was dropped because of pending overdue app switch"; + break; + case DROP_REASON_BLOCKED: + ALOGI("Dropped event because the current application is not responding and the user " + "has started interacting with a different application."); + reason = "inbound event was dropped because the current application is not responding " + "and the user has started interacting with a different application"; + break; + case DROP_REASON_STALE: + ALOGI("Dropped event because it is stale."); + reason = "inbound event was dropped because it is stale"; + break; + default: + ALOG_ASSERT(false); + return; + } + + switch (entry->type) { + case EventEntry::TYPE_KEY: { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + break; + } + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast(entry); + if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + } else { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + } + break; + } + } +} + +static bool isAppSwitchKeyCode(int32_t keyCode) { + return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL || + keyCode == AKEYCODE_APP_SWITCH; +} + +bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) { + return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) && + (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) && + (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); +} + +bool InputDispatcher::isAppSwitchPendingLocked() { + return mAppSwitchDueTime != LONG_LONG_MAX; +} + +void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { + mAppSwitchDueTime = LONG_LONG_MAX; + +#if DEBUG_APP_SWITCH + if (handled) { + ALOGD("App switch has arrived."); + } else { + ALOGD("App switch was abandoned."); + } +#endif +} + +bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { + return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT; +} + +bool InputDispatcher::haveCommandsLocked() const { + return !mCommandQueue.empty(); +} + +bool InputDispatcher::runCommandsLockedInterruptible() { + if (mCommandQueue.empty()) { + return false; + } + + do { + std::unique_ptr commandEntry = std::move(mCommandQueue.front()); + mCommandQueue.pop_front(); + Command command = commandEntry->command; + command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible' + + commandEntry->connection.clear(); + } while (!mCommandQueue.empty()); + return true; +} + +void InputDispatcher::postCommandLocked(std::unique_ptr commandEntry) { + mCommandQueue.push_back(std::move(commandEntry)); +} + +void InputDispatcher::drainInboundQueueLocked() { + while (!mInboundQueue.empty()) { + EventEntry* entry = mInboundQueue.front(); + mInboundQueue.pop_front(); + releaseInboundEventLocked(entry); + } + traceInboundQueueLengthLocked(); +} + +void InputDispatcher::releasePendingEventLocked() { + if (mPendingEvent) { + resetANRTimeoutsLocked(); + releaseInboundEventLocked(mPendingEvent); + mPendingEvent = nullptr; + } +} + +void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { + InjectionState* injectionState = entry->injectionState; + if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("Injected inbound event was dropped."); +#endif + setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED); + } + if (entry == mNextUnblockedEvent) { + mNextUnblockedEvent = nullptr; + } + addRecentEventLocked(entry); + entry->release(); +} + +void InputDispatcher::resetKeyRepeatLocked() { + if (mKeyRepeatState.lastKeyEntry) { + mKeyRepeatState.lastKeyEntry->release(); + mKeyRepeatState.lastKeyEntry = nullptr; + } +} + +InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { + KeyEntry* entry = mKeyRepeatState.lastKeyEntry; + + // Reuse the repeated key entry if it is otherwise unreferenced. + uint32_t policyFlags = entry->policyFlags & + (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); + if (entry->refCount == 1) { + entry->recycle(); + entry->eventTime = currentTime; + entry->policyFlags = policyFlags; + entry->repeatCount += 1; + } else { + KeyEntry* newEntry = + new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId, + entry->source, entry->displayId, policyFlags, entry->action, + entry->flags, entry->keyCode, entry->scanCode, entry->metaState, + entry->repeatCount + 1, entry->downTime); + + mKeyRepeatState.lastKeyEntry = newEntry; + entry->release(); + + entry = newEntry; + } + entry->syntheticRepeat = true; + + // Increment reference count since we keep a reference to the event in + // mKeyRepeatState.lastKeyEntry in addition to the one we return. + entry->refCount += 1; + + mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay; + return entry; +} + +bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, + ConfigurationChangedEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime); +#endif + + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); + + // Enqueue a command to run outside the lock to tell the policy that the configuration changed. + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); + commandEntry->eventTime = entry->eventTime; + postCommandLocked(std::move(commandEntry)); + return true; +} + +bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime, + entry->deviceId); +#endif + + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset"); + options.deviceId = entry->deviceId; + synthesizeCancelationEventsForAllConnectionsLocked(options); + return true; +} + +bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) { + // Preprocessing. + if (!entry->dispatchInProgress) { + if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && + (entry->policyFlags & POLICY_FLAG_TRUSTED) && + (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { + if (mKeyRepeatState.lastKeyEntry && + mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + // We have seen two identical key downs in a row which indicates that the device + // driver is automatically generating key repeats itself. We take note of the + // repeat here, but we disable our own next key repeat timer since it is clear that + // we will not need to synthesize key repeats ourselves. + entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves + } else { + // Not a repeat. Save key down state in case we do see a repeat later. + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout; + } + mKeyRepeatState.lastKeyEntry = entry; + entry->refCount += 1; + } else if (!entry->syntheticRepeat) { + resetKeyRepeatLocked(); + } + + if (entry->repeatCount == 1) { + entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; + } else { + entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; + } + + entry->dispatchInProgress = true; + + logOutboundKeyDetails("dispatchKey - ", entry); + } + + // Handle case where the policy asked us to try again later last time. + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { + if (currentTime < entry->interceptKeyWakeupTime) { + if (entry->interceptKeyWakeupTime < *nextWakeupTime) { + *nextWakeupTime = entry->interceptKeyWakeupTime; + } + return false; // wait until next wakeup + } + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + entry->interceptKeyWakeupTime = 0; + } + + // Give the policy a chance to intercept the key. + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { + if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); + sp focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); + if (focusedWindowHandle != nullptr) { + commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); + } + commandEntry->keyEntry = entry; + postCommandLocked(std::move(commandEntry)); + entry->refCount += 1; + return false; // wait for the command to run + } else { + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + } + } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + if (*dropReason == DROP_REASON_NOT_DROPPED) { + *dropReason = DROP_REASON_POLICY; + } + } + + // Clean up if dropping the event. + if (*dropReason != DROP_REASON_NOT_DROPPED) { + setInjectionResult(entry, + *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + : INPUT_EVENT_INJECTION_FAILED); + mReporter->reportDroppedKey(entry->sequenceNum); + return true; + } + + // Identify targets. + std::vector inputTargets; + int32_t injectionResult = + findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResult(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } + + // Add monitor channels from event's or focused display. + addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); + + // Dispatch the key. + dispatchEventLocked(currentTime, entry, inputTargets); + return true; +} + +void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " + "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " + "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, + prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, + entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, + entry->metaState, entry->repeatCount, entry->downTime); +#endif +} + +bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) { + ATRACE_CALL(); + // Preprocessing. + if (!entry->dispatchInProgress) { + entry->dispatchInProgress = true; + + logOutboundMotionDetails("dispatchMotion - ", entry); + } + + // Clean up if dropping the event. + if (*dropReason != DROP_REASON_NOT_DROPPED) { + setInjectionResult(entry, + *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + : INPUT_EVENT_INJECTION_FAILED); + return true; + } + + bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; + + // Identify targets. + std::vector inputTargets; + + bool conflictingPointerActions = false; + int32_t injectionResult; + if (isPointerEvent) { + // Pointer event. (eg. touchscreen) + injectionResult = + findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, + &conflictingPointerActions); + } else { + // Non touch event. (eg. trackball) + injectionResult = + findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); + } + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResult(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) { + CancelationOptions::Mode mode(isPointerEvent + ? CancelationOptions::CANCEL_POINTER_EVENTS + : CancelationOptions::CANCEL_NON_POINTER_EVENTS); + CancelationOptions options(mode, "input event injection failed"); + synthesizeCancelationEventsForMonitorsLocked(options); + } + return true; + } + + // Add monitor channels from event's or focused display. + addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); + + if (isPointerEvent) { + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId); + if (stateIndex >= 0) { + const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex); + if (!state.portalWindows.empty()) { + // The event has gone through these portal windows, so we add monitoring targets of + // the corresponding displays as well. + for (size_t i = 0; i < state.portalWindows.size(); i++) { + const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo(); + addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId, + -windowInfo->frameLeft, -windowInfo->frameTop); + } + } + } + } + + // Dispatch the motion. + if (conflictingPointerActions) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "conflicting pointer actions"); + synthesizeCancelationEventsForAllConnectionsLocked(options); + } + dispatchEventLocked(currentTime, entry, inputTargets); + return true; +} + +void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, " + "metaState=0x%x, buttonState=0x%x," + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, + entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState, + entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, + entry->downTime); + + for (uint32_t i = 0; i < entry->pointerCount; i++) { + ALOGD(" Pointer %d: id=%d, toolType=%d, " + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType, + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + } +#endif +} + +void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, + const std::vector& inputTargets) { + ATRACE_CALL(); +#if DEBUG_DISPATCH_CYCLE + ALOGD("dispatchEventToCurrentInputTargets"); +#endif + + ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true + + pokeUserActivityLocked(eventEntry); + + for (const InputTarget& inputTarget : inputTargets) { + sp connection = getConnectionLocked(inputTarget.inputChannel); + if (connection != nullptr) { + prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); + } else { +#if DEBUG_FOCUS + ALOGD("Dropping event delivery to target with channel '%s' because it " + "is no longer registered with the input dispatcher.", + inputTarget.inputChannel->getName().c_str()); +#endif + } + } +} + +int32_t InputDispatcher::handleTargetsNotReadyLocked( + nsecs_t currentTime, const EventEntry* entry, + const sp& applicationHandle, + const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { + if (applicationHandle == nullptr && windowHandle == nullptr) { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { +#if DEBUG_FOCUS + ALOGD("Waiting for system to become ready for input. Reason: %s", reason); +#endif + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = LONG_LONG_MAX; + mInputTargetWaitTimeoutExpired = false; + mInputTargetWaitApplicationToken.clear(); + } + } else { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { +#if DEBUG_FOCUS + ALOGD("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); +#endif + nsecs_t timeout; + if (windowHandle != nullptr) { + timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); + } else if (applicationHandle != nullptr) { + timeout = + applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); + } else { + timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; + } + + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutExpired = false; + mInputTargetWaitApplicationToken.clear(); + + if (windowHandle != nullptr) { + mInputTargetWaitApplicationToken = windowHandle->getApplicationToken(); + } + if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) { + mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken(); + } + } + } + + if (mInputTargetWaitTimeoutExpired) { + return INPUT_EVENT_INJECTION_TIMED_OUT; + } + + if (currentTime >= mInputTargetWaitTimeoutTime) { + onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, + mInputTargetWaitStartTime, reason); + + // Force poll loop to wake up immediately on next iteration once we get the + // ANR response back from the policy. + *nextWakeupTime = LONG_LONG_MIN; + return INPUT_EVENT_INJECTION_PENDING; + } else { + // Force poll loop to wake up when timeout is due. + if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { + *nextWakeupTime = mInputTargetWaitTimeoutTime; + } + return INPUT_EVENT_INJECTION_PENDING; + } +} + +void InputDispatcher::removeWindowByTokenLocked(const sp& token) { + for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { + TouchState& state = mTouchStatesByDisplay.editValueAt(d); + state.removeWindowByToken(token); + } +} + +void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( + nsecs_t newTimeout, const sp& inputChannel) { + if (newTimeout > 0) { + // Extend the timeout. + mInputTargetWaitTimeoutTime = now() + newTimeout; + } else { + // Give up. + mInputTargetWaitTimeoutExpired = true; + + // Input state will not be realistic. Mark it out of sync. + sp connection = getConnectionLocked(inputChannel); + if (connection != nullptr) { + sp token = connection->inputChannel->getToken(); + + if (token != nullptr) { + removeWindowByTokenLocked(token); + } + + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); + } + } + } +} + +nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) { + if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { + return currentTime - mInputTargetWaitStartTime; + } + return 0; +} + +void InputDispatcher::resetANRTimeoutsLocked() { +#if DEBUG_FOCUS + ALOGD("Resetting ANR timeouts."); +#endif + + // Reset input target wait timeout. + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; + mInputTargetWaitApplicationToken.clear(); +} + +/** + * Get the display id that the given event should go to. If this event specifies a valid display id, + * then it should be dispatched to that display. Otherwise, the event goes to the focused display. + * Focused display is the display that the user most recently interacted with. + */ +int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { + int32_t displayId; + switch (entry->type) { + case EventEntry::TYPE_KEY: { + const KeyEntry* typedEntry = static_cast(entry); + displayId = typedEntry->displayId; + break; + } + case EventEntry::TYPE_MOTION: { + const MotionEntry* typedEntry = static_cast(entry); + displayId = typedEntry->displayId; + break; + } + default: { + ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); + return ADISPLAY_ID_NONE; + } + } + return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; +} + +int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, + const EventEntry* entry, + std::vector& inputTargets, + nsecs_t* nextWakeupTime) { + int32_t injectionResult; + std::string reason; + + int32_t displayId = getTargetDisplayId(entry); + sp focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + sp focusedApplicationHandle = + getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + + // If there is no currently focused window and no focused application + // then drop the event. + if (focusedWindowHandle == nullptr) { + if (focusedApplicationHandle != nullptr) { + injectionResult = + handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + nullptr, nextWakeupTime, + "Waiting because no window has focus but there is " + "a focused application that may eventually add a " + "window when it finishes starting up."); + goto Unresponsive; + } + + ALOGI("Dropping event because there is no focused window or focused application in display " + "%" PRId32 ".", + displayId); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; + } + + // Check permissions. + if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // Check whether the window is ready for more input. + reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); + if (!reason.empty()) { + injectionResult = + handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + focusedWindowHandle, nextWakeupTime, reason.c_str()); + goto Unresponsive; + } + + // Success! Output targets. + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + addWindowTargetLocked(focusedWindowHandle, + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, + BitSet32(0), inputTargets); + + // Done. +Failed: +Unresponsive: + nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); + updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); +#if DEBUG_FOCUS + ALOGD("findFocusedWindow finished: injectionResult=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, timeSpentWaitingForApplication / 1000000.0); +#endif + return injectionResult; +} + +int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, + const MotionEntry* entry, + std::vector& inputTargets, + nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions) { + ATRACE_CALL(); + enum InjectionPermission { + INJECTION_PERMISSION_UNKNOWN, + INJECTION_PERMISSION_GRANTED, + INJECTION_PERMISSION_DENIED + }; + + // For security reasons, we defer updating the touch state until we are sure that + // event injection will be allowed. + int32_t displayId = entry->displayId; + int32_t action = entry->action; + int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; + + // Update the touch state as needed based on the properties of the touch event. + int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; + InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; + sp newHoverWindowHandle; + + // Copy current touch state into mTempTouchState. + // This state is always reset at the end of this function, so if we don't find state + // for the specified display then our initial state will be empty. + const TouchState* oldState = nullptr; + ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (oldStateIndex >= 0) { + oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex); + mTempTouchState.copyFrom(*oldState); + } + + bool isSplit = mTempTouchState.split; + bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 && + (mTempTouchState.deviceId != entry->deviceId || + mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId); + bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || + maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); + bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || + maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); + const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; + bool wrongDevice = false; + if (newGesture) { + bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; + if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { +#if DEBUG_FOCUS + ALOGD("Dropping event because a pointer for a different device is already down " + "in display %" PRId32, + displayId); +#endif + // TODO: test multiple simultaneous input streams. + injectionResult = INPUT_EVENT_INJECTION_FAILED; + switchedDevice = false; + wrongDevice = true; + goto Failed; + } + mTempTouchState.reset(); + mTempTouchState.down = down; + mTempTouchState.deviceId = entry->deviceId; + mTempTouchState.source = entry->source; + mTempTouchState.displayId = displayId; + isSplit = false; + } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { +#if DEBUG_FOCUS + ALOGI("Dropping move event because a pointer for a different device is already active " + "in display %" PRId32, + displayId); +#endif + // TODO: test multiple simultaneous input streams. + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + switchedDevice = false; + wrongDevice = true; + goto Failed; + } + + if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { + /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ + + int32_t x; + int32_t y; + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + // Always dispatch mouse events to cursor position. + if (isFromMouse) { + x = int32_t(entry->xCursorPosition); + y = int32_t(entry->yCursorPosition); + } else { + x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + } + bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; + sp newTouchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/, + true /*addPortalWindows*/); + + std::vector newGestureMonitors = isDown + ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows) + : std::vector{}; + + // Figure out whether splitting will be allowed for this window. + if (newTouchedWindowHandle != nullptr && + newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { + // New window supports splitting, but we should never split mouse events. + isSplit = !isFromMouse; + } else if (isSplit) { + // New window does not support splitting but we have already split events. + // Ignore the new window. + newTouchedWindowHandle = nullptr; + } + + // Handle the case where we did not find a window. + if (newTouchedWindowHandle == nullptr) { + // Try to assign the pointer to the first foreground window we find, if there is one. + newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); + } + + if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { + ALOGI("Dropping event because there is no touchable window or gesture monitor at " + "(%d, %d) in display %" PRId32 ".", + x, y, displayId); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; + } + + if (newTouchedWindowHandle != nullptr) { + // Set target flags. + int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS; + if (isSplit) { + targetFlags |= InputTarget::FLAG_SPLIT; + } + if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { + targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { + targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + } + + // Update hover state. + if (isHoverAction) { + newHoverWindowHandle = newTouchedWindowHandle; + } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + newHoverWindowHandle = mLastHoverWindowHandle; + } + + // Update the temporary touch state. + BitSet32 pointerIds; + if (isSplit) { + uint32_t pointerId = entry->pointerProperties[pointerIndex].id; + pointerIds.markBit(pointerId); + } + mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + } + + mTempTouchState.addGestureMonitors(newGestureMonitors); + } else { + /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ + + // If the pointer is not currently down, then ignore the event. + if (!mTempTouchState.down) { +#if DEBUG_FOCUS + ALOGD("Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display %" PRId32, + displayId); +#endif + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; + } + + // Check whether touches should slip outside of the current foreground window. + if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 && + mTempTouchState.isSlippery()) { + int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + + sp oldTouchedWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + sp newTouchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y); + if (oldTouchedWindowHandle != newTouchedWindowHandle && + oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { +#if DEBUG_FOCUS + ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, + oldTouchedWindowHandle->getName().c_str(), + newTouchedWindowHandle->getName().c_str(), displayId); +#endif + // Make a slippery exit from the old window. + mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + BitSet32(0)); + + // Make a slippery entrance into the new window. + if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { + isSplit = true; + } + + int32_t targetFlags = + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; + if (isSplit) { + targetFlags |= InputTarget::FLAG_SPLIT; + } + if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { + targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } + + BitSet32 pointerIds; + if (isSplit) { + pointerIds.markBit(entry->pointerProperties[0].id); + } + mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + } + } + } + + if (newHoverWindowHandle != mLastHoverWindowHandle) { + // Let the previous window know that the hover sequence is over. + if (mLastHoverWindowHandle != nullptr) { +#if DEBUG_HOVER + ALOGD("Sending hover exit event to window %s.", + mLastHoverWindowHandle->getName().c_str()); +#endif + mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, + BitSet32(0)); + } + + // Let the new window know that the hover sequence is starting. + if (newHoverWindowHandle != nullptr) { +#if DEBUG_HOVER + ALOGD("Sending hover enter event to window %s.", + newHoverWindowHandle->getName().c_str()); +#endif + mTempTouchState.addOrUpdateWindow(newHoverWindowHandle, + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, + BitSet32(0)); + } + } + + // Check permission to inject into all touched foreground windows and ensure there + // is at least one touched foreground window. + { + bool haveForegroundWindow = false; + for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { + haveForegroundWindow = true; + if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; + } + } + } + bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); + if (!haveForegroundWindow && !hasGestureMonitor) { +#if DEBUG_FOCUS + ALOGD("Dropping event because there is no touched foreground window in display %" PRId32 + " or gesture monitor to receive it.", + displayId); +#endif + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; + } + + // Permission granted to injection into all touched foreground windows. + injectionPermission = INJECTION_PERMISSION_GRANTED; + } + + // Check whether windows listening for outside touches are owned by the same UID. If it is + // set the policy flag that we will not reveal coordinate information to this window. + if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + sp foregroundWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + if (foregroundWindowHandle) { + const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; + for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + sp inputWindowHandle = touchedWindow.windowHandle; + if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) { + mTempTouchState.addOrUpdateWindow(inputWindowHandle, + InputTarget::FLAG_ZERO_COORDS, + BitSet32(0)); + } + } + } + } + } + + // Ensure all touched foreground windows are ready for new input. + for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { + // Check whether the window is ready for more input. + std::string reason = + checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, + entry, "touched"); + if (!reason.empty()) { + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr, + touchedWindow.windowHandle, + nextWakeupTime, reason.c_str()); + goto Unresponsive; + } + } + } + + // If this is the first pointer going down and the touched window has a wallpaper + // then also add the touched wallpaper windows so they are locked in for the duration + // of the touch gesture. + // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper + // engine only supports touch events. We would need to add a mechanism similar + // to View.onGenericMotionEvent to enable wallpapers to handle these events. + if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + sp foregroundWindowHandle = + mTempTouchState.getFirstForegroundWindowHandle(); + if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) { + const std::vector> windowHandles = + getWindowHandlesLocked(displayId); + for (const sp& windowHandle : windowHandles) { + const InputWindowInfo* info = windowHandle->getInfo(); + if (info->displayId == displayId && + windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) { + mTempTouchState + .addOrUpdateWindow(windowHandle, + InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget:: + FLAG_WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::FLAG_DISPATCH_AS_IS, + BitSet32(0)); + } + } + } + } + + // Success! Output targets. + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + + for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, + touchedWindow.pointerIds, inputTargets); + } + + for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) { + addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, + touchedMonitor.yOffset, inputTargets); + } + + // Drop the outside or hover touch windows since we will not care about them + // in the next iteration. + mTempTouchState.filterNonAsIsTouchWindows(); + +Failed: + // Check injection permission once and for all. + if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { + if (checkInjectionPermission(nullptr, entry->injectionState)) { + injectionPermission = INJECTION_PERMISSION_GRANTED; + } else { + injectionPermission = INJECTION_PERMISSION_DENIED; + } + } + + // Update final pieces of touch state if the injector had permission. + if (injectionPermission == INJECTION_PERMISSION_GRANTED) { + if (!wrongDevice) { + if (switchedDevice) { +#if DEBUG_FOCUS + ALOGD("Conflicting pointer actions: Switched to a different device."); +#endif + *outConflictingPointerActions = true; + } + + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (oldState && oldState->down) { +#if DEBUG_FOCUS + ALOGD("Conflicting pointer actions: Hover received while pointer was down."); +#endif + *outConflictingPointerActions = true; + } + mTempTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mTempTouchState.deviceId = entry->deviceId; + mTempTouchState.source = entry->source; + mTempTouchState.displayId = displayId; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + // All pointers up or canceled. + mTempTouchState.reset(); + } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + // First pointer went down. + if (oldState && oldState->down) { +#if DEBUG_FOCUS + ALOGD("Conflicting pointer actions: Down received while already down."); +#endif + *outConflictingPointerActions = true; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + // One pointer went up. + if (isSplit) { + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry->pointerProperties[pointerIndex].id; + + for (size_t i = 0; i < mTempTouchState.windows.size();) { + TouchedWindow& touchedWindow = mTempTouchState.windows[i]; + if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i); + continue; + } + } + i += 1; + } + } + } + + // Save changes unless the action was scroll in which case the temporary touch + // state was only valid for this one action. + if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { + if (mTempTouchState.displayId >= 0) { + if (oldStateIndex >= 0) { + mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState); + } else { + mTouchStatesByDisplay.add(displayId, mTempTouchState); + } + } else if (oldStateIndex >= 0) { + mTouchStatesByDisplay.removeItemsAt(oldStateIndex); + } + } + + // Update hover state. + mLastHoverWindowHandle = newHoverWindowHandle; + } + } else { +#if DEBUG_FOCUS + ALOGD("Not updating touch focus because injection was denied."); +#endif + } + +Unresponsive: + // Reset temporary touch state to ensure we release unnecessary references to input channels. + mTempTouchState.reset(); + + nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); + updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); +#if DEBUG_FOCUS + ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); +#endif + return injectionResult; +} + +void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, + int32_t targetFlags, BitSet32 pointerIds, + std::vector& inputTargets) { + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + InputTarget target; + target.inputChannel = inputChannel; + target.flags = targetFlags; + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; + target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + target.pointerIds = pointerIds; + inputTargets.push_back(target); +} + +void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, + int32_t displayId, float xOffset, + float yOffset) { + std::unordered_map>::const_iterator it = + mGlobalMonitorsByDisplay.find(displayId); + + if (it != mGlobalMonitorsByDisplay.end()) { + const std::vector& monitors = it->second; + for (const Monitor& monitor : monitors) { + addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets); + } + } +} + +void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset, + float yOffset, + std::vector& inputTargets) { + InputTarget target; + target.inputChannel = monitor.inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.xOffset = xOffset; + target.yOffset = yOffset; + target.pointerIds.clear(); + target.globalScaleFactor = 1.0f; + inputTargets.push_back(target); +} + +bool InputDispatcher::checkInjectionPermission(const sp& windowHandle, + const InjectionState* injectionState) { + if (injectionState && + (windowHandle == nullptr || + windowHandle->getInfo()->ownerUid != injectionState->injectorUid) && + !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { + if (windowHandle != nullptr) { + ALOGW("Permission denied: injecting event from pid %d uid %d to window %s " + "owned by uid %d", + injectionState->injectorPid, injectionState->injectorUid, + windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid); + } else { + ALOGW("Permission denied: injecting event from pid %d uid %d", + injectionState->injectorPid, injectionState->injectorUid); + } + return false; + } + return true; +} + +bool InputDispatcher::isWindowObscuredAtPointLocked(const sp& windowHandle, + int32_t x, int32_t y) const { + int32_t displayId = windowHandle->getInfo()->displayId; + const std::vector> windowHandles = getWindowHandlesLocked(displayId); + for (const sp& otherHandle : windowHandles) { + if (otherHandle == windowHandle) { + break; + } + + const InputWindowInfo* otherInfo = otherHandle->getInfo(); + if (otherInfo->displayId == displayId && otherInfo->visible && + !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) { + return true; + } + } + return false; +} + +bool InputDispatcher::isWindowObscuredLocked(const sp& windowHandle) const { + int32_t displayId = windowHandle->getInfo()->displayId; + const std::vector> windowHandles = getWindowHandlesLocked(displayId); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + for (const sp& otherHandle : windowHandles) { + if (otherHandle == windowHandle) { + break; + } + + const InputWindowInfo* otherInfo = otherHandle->getInfo(); + if (otherInfo->displayId == displayId && otherInfo->visible && + !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) { + return true; + } + } + return false; +} + +std::string InputDispatcher::checkWindowReadyForMoreInputLocked( + nsecs_t currentTime, const sp& windowHandle, + const EventEntry* eventEntry, const char* targetType) { + // If the window is paused then keep waiting. + if (windowHandle->getInfo()->paused) { + return StringPrintf("Waiting because the %s window is paused.", targetType); + } + + // If the window's connection is not registered then keep waiting. + sp connection = + getConnectionLocked(getInputChannelLocked(windowHandle->getToken())); + if (connection == nullptr) { + return StringPrintf("Waiting because the %s window's input channel is not " + "registered with the input dispatcher. The window may be in the " + "process of being removed.", + targetType); + } + + // If the connection is dead then keep waiting. + if (connection->status != Connection::STATUS_NORMAL) { + return StringPrintf("Waiting because the %s window's input connection is %s." + "The window may be in the process of being removed.", + targetType, connection->getStatusLabel()); + } + + // If the connection is backed up then keep waiting. + if (connection->inputPublisherBlocked) { + return StringPrintf("Waiting because the %s window's input channel is full. " + "Outbound queue length: %zu. Wait queue length: %zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); + } + + // Ensure that the dispatch queues aren't too far backed up for this event. + if (eventEntry->type == EventEntry::TYPE_KEY) { + // If the event is a key event, then we must wait for all previous events to + // complete before delivering it because previous events may have the + // side-effect of transferring focus to a different window and we want to + // ensure that the following keys are sent to the new window. + // + // Suppose the user touches a button in a window then immediately presses "A". + // If the button causes a pop-up window to appear then we want to ensure that + // the "A" key is delivered to the new pop-up window. This is because users + // often anticipate pending UI changes when typing on a keyboard. + // To obtain this behavior, we must serialize key events with respect to all + // prior input events. + if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { + return StringPrintf("Waiting to send key event because the %s window has not " + "finished processing all of the input events that were previously " + "delivered to it. Outbound queue length: %zu. Wait queue length: " + "%zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); + } + } else { + // Touch events can always be sent to a window immediately because the user intended + // to touch whatever was visible at the time. Even if focus changes or a new + // window appears moments later, the touch event was meant to be delivered to + // whatever window happened to be on screen at the time. + // + // Generic motion events, such as trackball or joystick events are a little trickier. + // Like key events, generic motion events are delivered to the focused window. + // Unlike key events, generic motion events don't tend to transfer focus to other + // windows and it is not important for them to be serialized. So we prefer to deliver + // generic motion events as soon as possible to improve efficiency and reduce lag + // through batching. + // + // The one case where we pause input event delivery is when the wait queue is piling + // up with lots of events because the application is not responding. + // This condition ensures that ANRs are detected reliably. + if (!connection->waitQueue.empty() && + currentTime >= + connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { + return StringPrintf("Waiting to send non-key event because the %s window has not " + "finished processing certain input events that were delivered to " + "it over " + "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " + "%0.1fms.", + targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, + connection->waitQueue.size(), + (currentTime - connection->waitQueue.front()->deliveryTime) * + 0.000001f); + } + } + return ""; +} + +std::string InputDispatcher::getApplicationWindowLabel( + const sp& applicationHandle, + const sp& windowHandle) { + if (applicationHandle != nullptr) { + if (windowHandle != nullptr) { + std::string label(applicationHandle->getName()); + label += " - "; + label += windowHandle->getName(); + return label; + } else { + return applicationHandle->getName(); + } + } else if (windowHandle != nullptr) { + return windowHandle->getName(); + } else { + return ""; + } +} + +void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { + int32_t displayId = getTargetDisplayId(eventEntry); + sp focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (focusedWindowHandle != nullptr) { + const InputWindowInfo* info = focusedWindowHandle->getInfo(); + if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); +#endif + return; + } + } + + int32_t eventType = USER_ACTIVITY_EVENT_OTHER; + switch (eventEntry->type) { + case EventEntry::TYPE_MOTION: { + const MotionEntry* motionEntry = static_cast(eventEntry); + if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { + return; + } + + if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { + eventType = USER_ACTIVITY_EVENT_TOUCH; + } + break; + } + case EventEntry::TYPE_KEY: { + const KeyEntry* keyEntry = static_cast(eventEntry); + if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { + return; + } + eventType = USER_ACTIVITY_EVENT_BUTTON; + break; + } + } + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doPokeUserActivityLockedInterruptible); + commandEntry->eventTime = eventEntry->eventTime; + commandEntry->userActivityEventType = eventType; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, + const sp& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", + connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + ATRACE_NAME(message.c_str()); + } +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " + "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " + "windowScaleFactor=(%f, %f), pointerIds=0x%x", + connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, + inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale, inputTarget->pointerIds.value); +#endif + + // Skip this event if the connection status is not normal. + // We don't want to enqueue additional outbound events if the connection is broken. + if (connection->status != Connection::STATUS_NORMAL) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Dropping event because the channel status is %s", + connection->getInputChannelName().c_str(), connection->getStatusLabel()); +#endif + return; + } + + // Split a motion event if needed. + if (inputTarget->flags & InputTarget::FLAG_SPLIT) { + ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); + + MotionEntry* originalMotionEntry = static_cast(eventEntry); + if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { + MotionEntry* splitMotionEntry = + splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); + if (!splitMotionEntry) { + return; // split event was dropped + } +#if DEBUG_FOCUS + ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); + logOutboundMotionDetails(" ", splitMotionEntry); +#endif + enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); + splitMotionEntry->release(); + return; + } + } + + // Not splitting. Enqueue dispatch entries for the event as is. + enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); +} + +void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, + const sp& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 + ")", + connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + ATRACE_NAME(message.c_str()); + } + + bool wasEmpty = connection->outboundQueue.empty(); + + // Enqueue dispatch entries for the requested modes. + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_IS); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); + + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty && !connection->outboundQueue.empty()) { + startDispatchCycleLocked(currentTime, connection); + } +} + +void InputDispatcher::enqueueDispatchEntryLocked(const sp& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget, + int32_t dispatchMode) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", + connection->getInputChannelName().c_str(), + dispatchModeToString(dispatchMode).c_str()); + ATRACE_NAME(message.c_str()); + } + int32_t inputTargetFlags = inputTarget->flags; + if (!(inputTargetFlags & dispatchMode)) { + return; + } + inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; + + // This is a new event. + // Enqueue a new dispatch entry onto the outbound queue for this connection. + DispatchEntry* dispatchEntry = + new DispatchEntry(eventEntry, // increments ref + inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale); + + // Apply target flags and update the connection's input state. + switch (eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast(eventEntry); + dispatchEntry->resolvedAction = keyEntry->action; + dispatchEntry->resolvedFlags = keyEntry->flags; + + if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", + connection->getInputChannelName().c_str()); +#endif + delete dispatchEntry; + return; // skip the inconsistent event + } + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast(eventEntry); + if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; + } else { + dispatchEntry->resolvedAction = motionEntry->action; + } + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && + !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source, + motionEntry->displayId)) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter " + "event", + connection->getInputChannelName().c_str()); +#endif + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } + + dispatchEntry->resolvedFlags = motionEntry->flags; + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + } + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + } + + if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion " + "event", + connection->getInputChannelName().c_str()); +#endif + delete dispatchEntry; + return; // skip the inconsistent event + } + + dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction, + inputTarget->inputChannel->getToken()); + + break; + } + } + + // Remember that we are waiting for this dispatch to complete. + if (dispatchEntry->hasForegroundTarget()) { + incrementPendingForegroundDispatches(eventEntry); + } + + // Enqueue the dispatch entry. + connection->outboundQueue.push_back(dispatchEntry); + traceOutboundQueueLength(connection); +} + +void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, + const sp& newToken) { + int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; + uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK; + if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) { + return; + } + + sp inputWindowHandle = getWindowHandleLocked(newToken); + if (inputWindowHandle == nullptr) { + return; + } + + sp focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); + + bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken; + + if (!hasFocusChanged) { + return; + } + + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); + commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, + const sp& connection) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)", + connection->getInputChannelName().c_str()); + ATRACE_NAME(message.c_str()); + } +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); +#endif + + while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.front(); + dispatchEntry->deliveryTime = currentTime; + + // Publish the event. + status_t status; + EventEntry* eventEntry = dispatchEntry->eventEntry; + switch (eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast(eventEntry); + + // Publish the key event. + status = connection->inputPublisher + .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, + keyEntry->source, keyEntry->displayId, + dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags, keyEntry->keyCode, + keyEntry->scanCode, keyEntry->metaState, + keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast(eventEntry); + + PointerCoords scaledCoords[MAX_POINTERS]; + const PointerCoords* usingCoords = motionEntry->pointerCoords; + + // Set the X and Y offset depending on the input source. + float xOffset, yOffset; + if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && + !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { + float globalScaleFactor = dispatchEntry->globalScaleFactor; + float wxs = dispatchEntry->windowXScale; + float wys = dispatchEntry->windowYScale; + xOffset = dispatchEntry->xOffset * wxs; + yOffset = dispatchEntry->yOffset * wys; + if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i] = motionEntry->pointerCoords[i]; + scaledCoords[i].scale(globalScaleFactor, wxs, wys); + } + usingCoords = scaledCoords; + } + } else { + xOffset = 0.0f; + yOffset = 0.0f; + + // We don't want the dispatch target to know. + if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i].clear(); + } + usingCoords = scaledCoords; + } + } + + // Publish the motion event. + status = connection->inputPublisher + .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, + motionEntry->source, motionEntry->displayId, + dispatchEntry->resolvedAction, + motionEntry->actionButton, + dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, + motionEntry->buttonState, + motionEntry->classification, xOffset, yOffset, + motionEntry->xPrecision, + motionEntry->yPrecision, + motionEntry->xCursorPosition, + motionEntry->yCursorPosition, + motionEntry->downTime, motionEntry->eventTime, + motionEntry->pointerCount, + motionEntry->pointerProperties, usingCoords); + break; + } + + default: + ALOG_ASSERT(false); + return; + } + + // Check the result. + if (status) { + if (status == WOULD_BLOCK) { + if (connection->waitQueue.empty()) { + ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " + "This is unexpected because the wait queue is empty, so the pipe " + "should be empty and we shouldn't have any problems writing an " + "event to it, status=%d", + connection->getInputChannelName().c_str(), status); + abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); + } else { + // Pipe is full and we are waiting for the app to finish process some events + // before sending more events to it. +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " + "waiting for the application to catch up", + connection->getInputChannelName().c_str()); +#endif + connection->inputPublisherBlocked = true; + } + } else { + ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " + "status=%d", + connection->getInputChannelName().c_str(), status); + abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); + } + return; + } + + // Re-enqueue the event on the wait queue. + connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), + connection->outboundQueue.end(), + dispatchEntry)); + traceOutboundQueueLength(connection); + connection->waitQueue.push_back(dispatchEntry); + traceWaitQueueLength(connection); + } +} + +void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, + const sp& connection, uint32_t seq, + bool handled) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", + connection->getInputChannelName().c_str(), seq, toString(handled)); +#endif + + connection->inputPublisherBlocked = false; + + if (connection->status == Connection::STATUS_BROKEN || + connection->status == Connection::STATUS_ZOMBIE) { + return; + } + + // Notify other system components and prepare to start the next dispatch cycle. + onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); +} + +void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, + const sp& connection, + bool notify) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s", + connection->getInputChannelName().c_str(), toString(notify)); +#endif + + // Clear the dispatch queues. + drainDispatchQueue(connection->outboundQueue); + traceOutboundQueueLength(connection); + drainDispatchQueue(connection->waitQueue); + traceWaitQueueLength(connection); + + // The connection appears to be unrecoverably broken. + // Ignore already broken or zombie connections. + if (connection->status == Connection::STATUS_NORMAL) { + connection->status = Connection::STATUS_BROKEN; + + if (notify) { + // Notify other system components. + onDispatchCycleBrokenLocked(currentTime, connection); + } + } +} + +void InputDispatcher::drainDispatchQueue(std::deque& queue) { + while (!queue.empty()) { + DispatchEntry* dispatchEntry = queue.front(); + queue.pop_front(); + releaseDispatchEntry(dispatchEntry); + } +} + +void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) { + if (dispatchEntry->hasForegroundTarget()) { + decrementPendingForegroundDispatches(dispatchEntry->eventEntry); + } + delete dispatchEntry; +} + +int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { + InputDispatcher* d = static_cast(data); + + { // acquire lock + std::scoped_lock _l(d->mLock); + + if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) { + ALOGE("Received spurious receive callback for unknown input channel. " + "fd=%d, events=0x%x", + fd, events); + return 0; // remove the callback + } + + bool notify; + sp connection = d->mConnectionsByFd[fd]; + if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { + if (!(events & ALOOPER_EVENT_INPUT)) { + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", + connection->getInputChannelName().c_str(), events); + return 1; + } + + nsecs_t currentTime = now(); + bool gotOne = false; + status_t status; + for (;;) { + uint32_t seq; + bool handled; + status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); + if (status) { + break; + } + d->finishDispatchCycleLocked(currentTime, connection, seq, handled); + gotOne = true; + } + if (gotOne) { + d->runCommandsLockedInterruptible(); + if (status == WOULD_BLOCK) { + return 1; + } + } + + notify = status != DEAD_OBJECT || !connection->monitor; + if (notify) { + ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", + connection->getInputChannelName().c_str(), status); + } + } else { + // Monitor channels are never explicitly unregistered. + // We do it automatically when the remote endpoint is closed so don't warn + // about them. + notify = !connection->monitor; + if (notify) { + ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " + "events=0x%x", + connection->getInputChannelName().c_str(), events); + } + } + + // Unregister the channel. + d->unregisterInputChannelLocked(connection->inputChannel, notify); + return 0; // remove the callback + } // release lock +} + +void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( + const CancelationOptions& options) { + for (const auto& pair : mConnectionsByFd) { + synthesizeCancelationEventsForConnectionLocked(pair.second, options); + } +} + +void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( + const CancelationOptions& options) { + synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay); + synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay); +} + +void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( + const CancelationOptions& options, + std::unordered_map>& monitorsByDisplay) { + for (const auto& it : monitorsByDisplay) { + const std::vector& monitors = it.second; + for (const Monitor& monitor : monitors) { + synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); + } + } +} + +void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( + const sp& channel, const CancelationOptions& options) { + sp connection = getConnectionLocked(channel); + if (connection == nullptr) { + return; + } + + synthesizeCancelationEventsForConnectionLocked(connection, options); +} + +void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( + const sp& connection, const CancelationOptions& options) { + if (connection->status == Connection::STATUS_BROKEN) { + return; + } + + nsecs_t currentTime = now(); + + std::vector cancelationEvents; + connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options); + + if (!cancelationEvents.empty()) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " + "with reality: %s, mode=%d.", + connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, + options.mode); +#endif + for (size_t i = 0; i < cancelationEvents.size(); i++) { + EventEntry* cancelationEventEntry = cancelationEvents[i]; + switch (cancelationEventEntry->type) { + case EventEntry::TYPE_KEY: + logOutboundKeyDetails("cancel - ", + static_cast(cancelationEventEntry)); + break; + case EventEntry::TYPE_MOTION: + logOutboundMotionDetails("cancel - ", + static_cast(cancelationEventEntry)); + break; + } + + InputTarget target; + sp windowHandle = + getWindowHandleLocked(connection->inputChannel->getToken()); + if (windowHandle != nullptr) { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; + target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + } else { + target.xOffset = 0; + target.yOffset = 0; + target.globalScaleFactor = 1.0f; + } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + + enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref + &target, InputTarget::FLAG_DISPATCH_AS_IS); + + cancelationEventEntry->release(); + } + + startDispatchCycleLocked(currentTime, connection); + } +} + +InputDispatcher::MotionEntry* InputDispatcher::splitMotionEvent( + const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { + ALOG_ASSERT(pointerIds.value != 0); + + uint32_t splitPointerIndexMap[MAX_POINTERS]; + PointerProperties splitPointerProperties[MAX_POINTERS]; + PointerCoords splitPointerCoords[MAX_POINTERS]; + + uint32_t originalPointerCount = originalMotionEntry->pointerCount; + uint32_t splitPointerCount = 0; + + for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; + originalPointerIndex++) { + const PointerProperties& pointerProperties = + originalMotionEntry->pointerProperties[originalPointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + if (pointerIds.hasBit(pointerId)) { + splitPointerIndexMap[splitPointerCount] = originalPointerIndex; + splitPointerProperties[splitPointerCount].copyFrom(pointerProperties); + splitPointerCoords[splitPointerCount].copyFrom( + originalMotionEntry->pointerCoords[originalPointerIndex]); + splitPointerCount += 1; + } + } + + if (splitPointerCount != pointerIds.count()) { + // This is bad. We are missing some of the pointers that we expected to deliver. + // Most likely this indicates that we received an ACTION_MOVE events that has + // different pointer ids than we expected based on the previous ACTION_DOWN + // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers + // in this way. + ALOGW("Dropping split motion event because the pointer count is %d but " + "we expected there to be %d pointers. This probably means we received " + "a broken sequence of pointer ids from the input device.", + splitPointerCount, pointerIds.count()); + return nullptr; + } + + int32_t action = originalMotionEntry->action; + int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); + const PointerProperties& pointerProperties = + originalMotionEntry->pointerProperties[originalPointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + if (pointerIds.hasBit(pointerId)) { + if (pointerIds.count() == 1) { + // The first/last pointer went down/up. + action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN + ? AMOTION_EVENT_ACTION_DOWN + : AMOTION_EVENT_ACTION_UP; + } else { + // A secondary pointer went down/up. + uint32_t splitPointerIndex = 0; + while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) { + splitPointerIndex += 1; + } + action = maskedAction | + (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + } else { + // An unrelated pointer changed. + action = AMOTION_EVENT_ACTION_MOVE; + } + } + + MotionEntry* splitMotionEntry = + new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, + originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, + action, originalMotionEntry->actionButton, originalMotionEntry->flags, + originalMotionEntry->metaState, originalMotionEntry->buttonState, + originalMotionEntry->classification, originalMotionEntry->edgeFlags, + originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, + originalMotionEntry->xCursorPosition, + originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, + splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); + + if (originalMotionEntry->injectionState) { + splitMotionEntry->injectionState = originalMotionEntry->injectionState; + splitMotionEntry->injectionState->refCount += 1; + } + + return splitMotionEntry; +} + +void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime); +#endif + + bool needWake; + { // acquire lock + std::scoped_lock _l(mLock); + + ConfigurationChangedEntry* newEntry = + new ConfigurationChangedEntry(args->sequenceNum, args->eventTime); + needWake = enqueueInboundEventLocked(newEntry); + } // release lock + + if (needWake) { + mLooper->wake(); + } +} + +/** + * If one of the meta shortcuts is detected, process them here: + * Meta + Backspace -> generate BACK + * Meta + Enter -> generate HOME + * This will potentially overwrite keyCode and metaState. + */ +void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, + int32_t& keyCode, int32_t& metaState) { + if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) { + int32_t newKeyCode = AKEYCODE_UNKNOWN; + if (keyCode == AKEYCODE_DEL) { + newKeyCode = AKEYCODE_BACK; + } else if (keyCode == AKEYCODE_ENTER) { + newKeyCode = AKEYCODE_HOME; + } + if (newKeyCode != AKEYCODE_UNKNOWN) { + std::scoped_lock _l(mLock); + struct KeyReplacement replacement = {keyCode, deviceId}; + mReplacedKeys.add(replacement, newKeyCode); + keyCode = newKeyCode; + metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } + } else if (action == AKEY_EVENT_ACTION_UP) { + // In order to maintain a consistent stream of up and down events, check to see if the key + // going up is one we've replaced in a down event and haven't yet replaced in an up event, + // even if the modifier was released between the down and the up events. + std::scoped_lock _l(mLock); + struct KeyReplacement replacement = {keyCode, deviceId}; + ssize_t index = mReplacedKeys.indexOfKey(replacement); + if (index >= 0) { + keyCode = mReplacedKeys.valueAt(index); + mReplacedKeys.removeItemsAt(index); + metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } + } +} + +void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + "policyFlags=0x%x, action=0x%x, " + "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->flags, args->keyCode, args->scanCode, args->metaState, + args->downTime); +#endif + if (!validateKeyEvent(args->action)) { + return; + } + + uint32_t policyFlags = args->policyFlags; + int32_t flags = args->flags; + int32_t metaState = args->metaState; + // InputDispatcher tracks and generates key repeats on behalf of + // whatever notifies it, so repeatCount should always be set to 0 + constexpr int32_t repeatCount = 0; + if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { + policyFlags |= POLICY_FLAG_VIRTUAL; + flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + } + if (policyFlags & POLICY_FLAG_FUNCTION) { + metaState |= AMETA_FUNCTION_ON; + } + + policyFlags |= POLICY_FLAG_TRUSTED; + + int32_t keyCode = args->keyCode; + accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); + + KeyEvent event; + event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, + args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); + + android::base::Timer t; + mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + + bool needWake; + { // acquire lock + mLock.lock(); + + if (shouldSendKeyToInputFilterLocked(args)) { + mLock.unlock(); + + policyFlags |= POLICY_FLAG_FILTERED; + if (!mPolicy->filterInputEvent(&event, policyFlags)) { + return; // event was consumed by the filter + } + + mLock.lock(); + } + + KeyEntry* newEntry = + new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, flags, keyCode, + args->scanCode, metaState, repeatCount, args->downTime); + + needWake = enqueueInboundEventLocked(newEntry); + mLock.unlock(); + } // release lock + + if (needWake) { + mLooper->wake(); + } +} + +bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) { + return mInputFilterEnabled; +} + +void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " + "yCursorPosition=%f, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->actionButton, args->flags, args->metaState, args->buttonState, + args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, + args->yCursorPosition, args->downTime); + for (uint32_t i = 0; i < args->pointerCount; i++) { + ALOGD(" Pointer %d: id=%d, toolType=%d, " + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, args->pointerProperties[i].id, args->pointerProperties[i].toolType, + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + } +#endif + if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, + args->pointerProperties)) { + return; + } + + uint32_t policyFlags = args->policyFlags; + policyFlags |= POLICY_FLAG_TRUSTED; + + android::base::Timer t; + mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + + bool needWake; + { // acquire lock + mLock.lock(); + + if (shouldSendMotionToInputFilterLocked(args)) { + mLock.unlock(); + + MotionEvent event; + event.initialize(args->deviceId, args->source, args->displayId, args->action, + args->actionButton, args->flags, args->edgeFlags, args->metaState, + args->buttonState, args->classification, 0, 0, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); + + policyFlags |= POLICY_FLAG_FILTERED; + if (!mPolicy->filterInputEvent(&event, policyFlags)) { + return; // event was consumed by the filter + } + + mLock.lock(); + } + + // Just enqueue a new motion event. + MotionEntry* newEntry = + new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, args->actionButton, + args->flags, args->metaState, args->buttonState, + args->classification, args->edgeFlags, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->pointerCount, args->pointerProperties, + args->pointerCoords, 0, 0); + + needWake = enqueueInboundEventLocked(newEntry); + mLock.unlock(); + } // release lock + + if (needWake) { + mLooper->wake(); + } +} + +bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) { + return mInputFilterEnabled; +} + +void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " + "switchMask=0x%08x", + args->eventTime, args->policyFlags, args->switchValues, args->switchMask); +#endif + + uint32_t policyFlags = args->policyFlags; + policyFlags |= POLICY_FLAG_TRUSTED; + mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags); +} + +void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime, + args->deviceId); +#endif + + bool needWake; + { // acquire lock + std::scoped_lock _l(mLock); + + DeviceResetEntry* newEntry = + new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId); + needWake = enqueueInboundEventLocked(newEntry); + } // release lock + + if (needWake) { + mLooper->wake(); + } +} + +int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, + int32_t timeoutMillis, uint32_t policyFlags) { +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " + "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", + event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); +#endif + + nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); + + policyFlags |= POLICY_FLAG_INJECTED; + if (hasInjectionPermission(injectorPid, injectorUid)) { + policyFlags |= POLICY_FLAG_TRUSTED; + } + + std::queue injectedEntries; + switch (event->getType()) { + case AINPUT_EVENT_TYPE_KEY: { + KeyEvent keyEvent; + keyEvent.initialize(*static_cast(event)); + int32_t action = keyEvent.getAction(); + if (!validateKeyEvent(action)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + int32_t flags = keyEvent.getFlags(); + int32_t keyCode = keyEvent.getKeyCode(); + int32_t metaState = keyEvent.getMetaState(); + accelerateMetaShortcuts(keyEvent.getDeviceId(), action, + /*byref*/ keyCode, /*byref*/ metaState); + keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), + keyEvent.getDisplayId(), action, flags, keyCode, + keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), + keyEvent.getDownTime(), keyEvent.getEventTime()); + + if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { + policyFlags |= POLICY_FLAG_VIRTUAL; + } + + if (!(policyFlags & POLICY_FLAG_FILTERED)) { + android::base::Timer t; + mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + } + + mLock.lock(); + KeyEntry* injectedEntry = + new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), + keyEvent.getDeviceId(), keyEvent.getSource(), + keyEvent.getDisplayId(), policyFlags, action, flags, + keyEvent.getKeyCode(), keyEvent.getScanCode(), + keyEvent.getMetaState(), keyEvent.getRepeatCount(), + keyEvent.getDownTime()); + injectedEntries.push(injectedEntry); + break; + } + + case AINPUT_EVENT_TYPE_MOTION: { + const MotionEvent* motionEvent = static_cast(event); + int32_t action = motionEvent->getAction(); + size_t pointerCount = motionEvent->getPointerCount(); + const PointerProperties* pointerProperties = motionEvent->getPointerProperties(); + int32_t actionButton = motionEvent->getActionButton(); + int32_t displayId = motionEvent->getDisplayId(); + if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + if (!(policyFlags & POLICY_FLAG_FILTERED)) { + nsecs_t eventTime = motionEvent->getEventTime(); + android::base::Timer t; + mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + } + + mLock.lock(); + const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); + const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); + MotionEntry* injectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); + injectedEntries.push(injectedEntry); + for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { + sampleEventTimes += 1; + samplePointerCoords += pointerCount; + MotionEntry* nextInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, + actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); + injectedEntries.push(nextInjectedEntry); + } + break; + } + + default: + ALOGW("Cannot inject event of type %d", event->getType()); + return INPUT_EVENT_INJECTION_FAILED; + } + + InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectionState->injectionIsAsync = true; + } + + injectionState->refCount += 1; + injectedEntries.back()->injectionState = injectionState; + + bool needWake = false; + while (!injectedEntries.empty()) { + needWake |= enqueueInboundEventLocked(injectedEntries.front()); + injectedEntries.pop(); + } + + mLock.unlock(); + + if (needWake) { + mLooper->wake(); + } + + int32_t injectionResult; + { // acquire lock + std::unique_lock _l(mLock); + + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + } else { + for (;;) { + injectionResult = injectionState->injectionResult; + if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + break; + } + + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + ALOGD("injectInputEvent - Timed out waiting for injection result " + "to become available."); +#endif + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); + } + + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED && + syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { + while (injectionState->pendingForegroundDispatches != 0) { +#if DEBUG_INJECTION + ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", + injectionState->pendingForegroundDispatches); +#endif + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + ALOGD("injectInputEvent - Timed out waiting for pending foreground " + "dispatches to finish."); +#endif + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); + } + } + } + + injectionState->release(); + } // release lock + +#if DEBUG_INJECTION + ALOGD("injectInputEvent - Finished with result %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, injectorPid, injectorUid); +#endif + + return injectionResult; +} + +bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { + return injectorUid == 0 || + mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); +} + +void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) { + InjectionState* injectionState = entry->injectionState; + if (injectionState) { +#if DEBUG_INJECTION + ALOGD("Setting input event injection result to %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, injectionState->injectorPid, injectionState->injectorUid); +#endif + + if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) { + // Log the outcome since the injector did not wait for the injection result. + switch (injectionResult) { + case INPUT_EVENT_INJECTION_SUCCEEDED: + ALOGV("Asynchronous input event injection succeeded."); + break; + case INPUT_EVENT_INJECTION_FAILED: + ALOGW("Asynchronous input event injection failed."); + break; + case INPUT_EVENT_INJECTION_PERMISSION_DENIED: + ALOGW("Asynchronous input event injection permission denied."); + break; + case INPUT_EVENT_INJECTION_TIMED_OUT: + ALOGW("Asynchronous input event injection timed out."); + break; + } + } + + injectionState->injectionResult = injectionResult; + mInjectionResultAvailable.notify_all(); + } +} + +void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) { + InjectionState* injectionState = entry->injectionState; + if (injectionState) { + injectionState->pendingForegroundDispatches += 1; + } +} + +void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { + InjectionState* injectionState = entry->injectionState; + if (injectionState) { + injectionState->pendingForegroundDispatches -= 1; + + if (injectionState->pendingForegroundDispatches == 0) { + mInjectionSyncFinished.notify_all(); + } + } +} + +std::vector> InputDispatcher::getWindowHandlesLocked( + int32_t displayId) const { + return getValueByKey(mWindowHandlesByDisplay, displayId); +} + +sp InputDispatcher::getWindowHandleLocked( + const sp& windowHandleToken) const { + for (auto& it : mWindowHandlesByDisplay) { + const std::vector> windowHandles = it.second; + for (const sp& windowHandle : windowHandles) { + if (windowHandle->getToken() == windowHandleToken) { + return windowHandle; + } + } + } + return nullptr; +} + +bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle) const { + for (auto& it : mWindowHandlesByDisplay) { + const std::vector> windowHandles = it.second; + for (const sp& handle : windowHandles) { + if (handle->getToken() == windowHandle->getToken()) { + if (windowHandle->getInfo()->displayId != it.first) { + ALOGE("Found window %s in display %" PRId32 + ", but it should belong to display %" PRId32, + windowHandle->getName().c_str(), it.first, + windowHandle->getInfo()->displayId); + } + return true; + } + } + } + return false; +} + +sp InputDispatcher::getInputChannelLocked(const sp& token) const { + size_t count = mInputChannelsByToken.count(token); + if (count == 0) { + return nullptr; + } + return mInputChannelsByToken.at(token); +} + +void InputDispatcher::updateWindowHandlesForDisplayLocked( + const std::vector>& inputWindowHandles, int32_t displayId) { + if (inputWindowHandles.empty()) { + // Remove all handles on a display if there are no windows left. + mWindowHandlesByDisplay.erase(displayId); + return; + } + + // Since we compare the pointer of input window handles across window updates, we need + // to make sure the handle object for the same window stays unchanged across updates. + const std::vector>& oldHandles = getWindowHandlesLocked(displayId); + std::unordered_map, sp, IBinderHash> oldHandlesByTokens; + for (const sp& handle : oldHandles) { + oldHandlesByTokens[handle->getToken()] = handle; + } + + std::vector> newHandles; + for (const sp& handle : inputWindowHandles) { + if (!handle->updateInfo()) { + // handle no longer valid + continue; + } + + const InputWindowInfo* info = handle->getInfo(); + if ((getInputChannelLocked(handle->getToken()) == nullptr && + info->portalToDisplayId == ADISPLAY_ID_NONE)) { + const bool noInputChannel = + info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; + const bool canReceiveInput = + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); + if (canReceiveInput && !noInputChannel) { + ALOGE("Window handle %s has no registered input channel", + handle->getName().c_str()); + } + continue; + } + + if (info->displayId != displayId) { + ALOGE("Window %s updated by wrong display %d, should belong to display %d", + handle->getName().c_str(), displayId, info->displayId); + continue; + } + + if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { + const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); + oldHandle->updateFrom(handle); + newHandles.push_back(oldHandle); + } else { + newHandles.push_back(handle); + } + } + + // Insert or replace + mWindowHandlesByDisplay[displayId] = newHandles; +} + +/** + * Called from InputManagerService, update window handle list by displayId that can receive input. + * A window handle contains information about InputChannel, Touch Region, Types, Focused,... + * If set an empty list, remove all handles from the specific display. + * For focused handle, check if need to change and send a cancel event to previous one. + * For removed handle, check if need to send a cancel event if already in touch. + */ +void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, + int32_t displayId, + const sp& setInputWindowsListener) { +#if DEBUG_FOCUS + ALOGD("setInputWindows displayId=%" PRId32, displayId); +#endif + { // acquire lock + std::scoped_lock _l(mLock); + + // Copy old handles for release if they are no longer present. + const std::vector> oldWindowHandles = + getWindowHandlesLocked(displayId); + + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + + sp newFocusedWindowHandle = nullptr; + bool foundHoveredWindow = false; + for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; + } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; + } + } + + if (!foundHoveredWindow) { + mLastHoverWindowHandle = nullptr; + } + + sp oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + + if (oldFocusedWindowHandle != newFocusedWindowHandle) { + if (oldFocusedWindowHandle != nullptr) { +#if DEBUG_FOCUS + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); +#endif + sp focusedInputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (focusedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + } + mFocusedWindowHandlesByDisplay.erase(displayId); + } + if (newFocusedWindowHandle != nullptr) { +#if DEBUG_FOCUS + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); +#endif + mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + } + + if (mFocusedDisplayId == displayId) { + onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + } + } + + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex >= 0) { + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + for (size_t i = 0; i < state.windows.size();) { + TouchedWindow& touchedWindow = state.windows[i]; + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { +#if DEBUG_FOCUS + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); +#endif + sp touchedInputChannel = + getInputChannelLocked(touchedWindow.windowHandle->getToken()); + if (touchedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, + options); + } + state.windows.erase(state.windows.begin() + i); + } else { + ++i; + } + } + } + + // Release information for windows that are no longer present. + // This ensures that unused input channels are released promptly. + // Otherwise, they might stick around until the window handle is destroyed + // which might not happen until the next GC. + for (const sp& oldWindowHandle : oldWindowHandles) { + if (!hasWindowHandleLocked(oldWindowHandle)) { +#if DEBUG_FOCUS + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); +#endif + oldWindowHandle->releaseChannel(); + } + } + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); + + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); + } +} + +void InputDispatcher::setFocusedApplication( + int32_t displayId, const sp& inputApplicationHandle) { +#if DEBUG_FOCUS + ALOGD("setFocusedApplication displayId=%" PRId32, displayId); +#endif + { // acquire lock + std::scoped_lock _l(mLock); + + sp oldFocusedApplicationHandle = + getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { + if (oldFocusedApplicationHandle != inputApplicationHandle) { + if (oldFocusedApplicationHandle != nullptr) { + resetANRTimeoutsLocked(); + } + mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; + } + } else if (oldFocusedApplicationHandle != nullptr) { + resetANRTimeoutsLocked(); + oldFocusedApplicationHandle.clear(); + mFocusedApplicationHandlesByDisplay.erase(displayId); + } + +#if DEBUG_FOCUS + // logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + +/** + * Sets the focused display, which is responsible for receiving focus-dispatched input events where + * the display not specified. + * + * We track any unreleased events for each window. If a window loses the ability to receive the + * released event, we will send a cancel event to it. So when the focused display is changed, we + * cancel all the unreleased display-unspecified events for the focused window on the old focused + * display. The display-specified events won't be affected. + */ +void InputDispatcher::setFocusedDisplay(int32_t displayId) { +#if DEBUG_FOCUS + ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); +#endif + { // acquire lock + std::scoped_lock _l(mLock); + + if (mFocusedDisplayId != displayId) { + sp oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); + if (oldFocusedWindowHandle != nullptr) { + sp inputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (inputChannel != nullptr) { + CancelationOptions + options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "The display which contains this window no longer has focus."); + options.displayId = ADISPLAY_ID_NONE; + synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + } + } + mFocusedDisplayId = displayId; + + // Sanity check + sp newFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + + if (newFocusedWindowHandle == nullptr) { + ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId); + if (!mFocusedWindowHandlesByDisplay.empty()) { + ALOGE("But another display has a focused window:"); + for (auto& it : mFocusedWindowHandlesByDisplay) { + const int32_t displayId = it.first; + const sp& windowHandle = it.second; + ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId, + windowHandle->getName().c_str()); + } + } + } + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + +void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { +#if DEBUG_FOCUS + ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); +#endif + + bool changed; + { // acquire lock + std::scoped_lock _l(mLock); + + if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { + if (mDispatchFrozen && !frozen) { + resetANRTimeoutsLocked(); + } + + if (mDispatchEnabled && !enabled) { + resetAndDropEverythingLocked("dispatcher is being disabled"); + } + + mDispatchEnabled = enabled; + mDispatchFrozen = frozen; + changed = true; + } else { + changed = false; + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + if (changed) { + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); + } +} + +void InputDispatcher::setInputFilterEnabled(bool enabled) { +#if DEBUG_FOCUS + ALOGD("setInputFilterEnabled: enabled=%d", enabled); +#endif + + { // acquire lock + std::scoped_lock _l(mLock); + + if (mInputFilterEnabled == enabled) { + return; + } + + mInputFilterEnabled = enabled; + resetAndDropEverythingLocked("input filter is being enabled or disabled"); + } // release lock + + // Wake up poll loop since there might be work to do to drop everything. + mLooper->wake(); +} + +bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp& toToken) { + if (fromToken == toToken) { +#if DEBUG_FOCUS + ALOGD("Trivial transfer to same window."); +#endif + return true; + } + + { // acquire lock + std::scoped_lock _l(mLock); + + sp fromWindowHandle = getWindowHandleLocked(fromToken); + sp toWindowHandle = getWindowHandleLocked(toToken); + if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { + ALOGW("Cannot transfer focus because from or to window not found."); + return false; + } +#if DEBUG_FOCUS + ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); +#endif + if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { +#if DEBUG_FOCUS + ALOGD("Cannot transfer focus because windows are on different displays."); +#endif + return false; + } + + bool found = false; + for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { + TouchState& state = mTouchStatesByDisplay.editValueAt(d); + for (size_t i = 0; i < state.windows.size(); i++) { + const TouchedWindow& touchedWindow = state.windows[i]; + if (touchedWindow.windowHandle == fromWindowHandle) { + int32_t oldTargetFlags = touchedWindow.targetFlags; + BitSet32 pointerIds = touchedWindow.pointerIds; + + state.windows.erase(state.windows.begin() + i); + + int32_t newTargetFlags = oldTargetFlags & + (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | + InputTarget::FLAG_DISPATCH_AS_IS); + state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); + + found = true; + goto Found; + } + } + } + Found: + + if (!found) { +#if DEBUG_FOCUS + ALOGD("Focus transfer failed because from window did not have focus."); +#endif + return false; + } + + sp fromChannel = getInputChannelLocked(fromToken); + sp toChannel = getInputChannelLocked(toToken); + sp fromConnection = getConnectionLocked(fromChannel); + sp toConnection = getConnectionLocked(toChannel); + if (fromConnection != nullptr && toConnection != nullptr) { + fromConnection->inputState.copyPointerStateTo(toConnection->inputState); + CancelationOptions + options(CancelationOptions::CANCEL_POINTER_EVENTS, + "transferring touch focus from this window to another window"); + synthesizeCancelationEventsForConnectionLocked(fromConnection, options); + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); + return true; +} + +void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { +#if DEBUG_FOCUS + ALOGD("Resetting and dropping all events (%s).", reason); +#endif + + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + + resetKeyRepeatLocked(); + releasePendingEventLocked(); + drainInboundQueueLocked(); + resetANRTimeoutsLocked(); + + mTouchStatesByDisplay.clear(); + mLastHoverWindowHandle.clear(); + mReplacedKeys.clear(); +} + +void InputDispatcher::logDispatchStateLocked() { + std::string dump; + dumpDispatchStateLocked(dump); + + std::istringstream stream(dump); + std::string line; + + while (std::getline(stream, line, '\n')) { + ALOGD("%s", line.c_str()); + } +} + +void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { + dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled)); + dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen)); + dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled)); + dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId); + + if (!mFocusedApplicationHandlesByDisplay.empty()) { + dump += StringPrintf(INDENT "FocusedApplications:\n"); + for (auto& it : mFocusedApplicationHandlesByDisplay) { + const int32_t displayId = it.first; + const sp& applicationHandle = it.second; + dump += StringPrintf(INDENT2 "displayId=%" PRId32 + ", name='%s', dispatchingTimeout=%0.3fms\n", + displayId, applicationHandle->getName().c_str(), + applicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) / + 1000000.0); + } + } else { + dump += StringPrintf(INDENT "FocusedApplications: \n"); + } + + if (!mFocusedWindowHandlesByDisplay.empty()) { + dump += StringPrintf(INDENT "FocusedWindows:\n"); + for (auto& it : mFocusedWindowHandlesByDisplay) { + const int32_t displayId = it.first; + const sp& windowHandle = it.second; + dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId, + windowHandle->getName().c_str()); + } + } else { + dump += StringPrintf(INDENT "FocusedWindows: \n"); + } + + if (!mTouchStatesByDisplay.isEmpty()) { + dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); + for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) { + const TouchState& state = mTouchStatesByDisplay.valueAt(i); + dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", + state.displayId, toString(state.down), toString(state.split), + state.deviceId, state.source); + if (!state.windows.empty()) { + dump += INDENT3 "Windows:\n"; + for (size_t i = 0; i < state.windows.size(); i++) { + const TouchedWindow& touchedWindow = state.windows[i]; + dump += StringPrintf(INDENT4 + "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", + i, touchedWindow.windowHandle->getName().c_str(), + touchedWindow.pointerIds.value, touchedWindow.targetFlags); + } + } else { + dump += INDENT3 "Windows: \n"; + } + if (!state.portalWindows.empty()) { + dump += INDENT3 "Portal windows:\n"; + for (size_t i = 0; i < state.portalWindows.size(); i++) { + const sp portalWindowHandle = state.portalWindows[i]; + dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i, + portalWindowHandle->getName().c_str()); + } + } + } + } else { + dump += INDENT "TouchStates: \n"; + } + + if (!mWindowHandlesByDisplay.empty()) { + for (auto& it : mWindowHandlesByDisplay) { + const std::vector> windowHandles = it.second; + dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); + if (!windowHandles.empty()) { + dump += INDENT2 "Windows:\n"; + for (size_t i = 0; i < windowHandles.size(); i++) { + const sp& windowHandle = windowHandles[i]; + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + + dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " + "portalToDisplayId=%d, paused=%s, hasFocus=%s, " + "hasWallpaper=%s, " + "visible=%s, canReceiveKeys=%s, flags=0x%08x, " + "type=0x%08x, layer=%d, " + "frame=[%d,%d][%d,%d], globalScale=%f, " + "windowScale=(%f,%f), " + "touchableRegion=", + i, windowInfo->name.c_str(), windowInfo->displayId, + windowInfo->portalToDisplayId, + toString(windowInfo->paused), + toString(windowInfo->hasFocus), + toString(windowInfo->hasWallpaper), + toString(windowInfo->visible), + toString(windowInfo->canReceiveKeys), + windowInfo->layoutParamsFlags, + windowInfo->layoutParamsType, windowInfo->layer, + windowInfo->frameLeft, windowInfo->frameTop, + windowInfo->frameRight, windowInfo->frameBottom, + windowInfo->globalScaleFactor, windowInfo->windowXScale, + windowInfo->windowYScale); + dumpRegion(dump, windowInfo->touchableRegion); + dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + windowInfo->ownerPid, windowInfo->ownerUid, + windowInfo->dispatchingTimeout / 1000000.0); + } + } else { + dump += INDENT2 "Windows: \n"; + } + } + } else { + dump += INDENT "Displays: \n"; + } + + if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) { + for (auto& it : mGlobalMonitorsByDisplay) { + const std::vector& monitors = it.second; + dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first); + dumpMonitors(dump, monitors); + } + for (auto& it : mGestureMonitorsByDisplay) { + const std::vector& monitors = it.second; + dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first); + dumpMonitors(dump, monitors); + } + } else { + dump += INDENT "Monitors: \n"; + } + + nsecs_t currentTime = now(); + + // Dump recently dispatched or dropped events from oldest to newest. + if (!mRecentQueue.empty()) { + dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); + for (EventEntry* entry : mRecentQueue) { + dump += INDENT2; + entry->appendDescription(dump); + dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + } + } else { + dump += INDENT "RecentQueue: \n"; + } + + // Dump event currently being dispatched. + if (mPendingEvent) { + dump += INDENT "PendingEvent:\n"; + dump += INDENT2; + mPendingEvent->appendDescription(dump); + dump += StringPrintf(", age=%0.1fms\n", + (currentTime - mPendingEvent->eventTime) * 0.000001f); + } else { + dump += INDENT "PendingEvent: \n"; + } + + // Dump inbound events from oldest to newest. + if (!mInboundQueue.empty()) { + dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size()); + for (EventEntry* entry : mInboundQueue) { + dump += INDENT2; + entry->appendDescription(dump); + dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + } + } else { + dump += INDENT "InboundQueue: \n"; + } + + if (!mReplacedKeys.isEmpty()) { + dump += INDENT "ReplacedKeys:\n"; + for (size_t i = 0; i < mReplacedKeys.size(); i++) { + const KeyReplacement& replacement = mReplacedKeys.keyAt(i); + int32_t newKeyCode = mReplacedKeys.valueAt(i); + dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i, + replacement.keyCode, replacement.deviceId, newKeyCode); + } + } else { + dump += INDENT "ReplacedKeys: \n"; + } + + if (!mConnectionsByFd.empty()) { + dump += INDENT "Connections:\n"; + for (const auto& pair : mConnectionsByFd) { + const sp& connection = pair.second; + dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " + "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + pair.first, connection->getInputChannelName().c_str(), + connection->getWindowName().c_str(), connection->getStatusLabel(), + toString(connection->monitor), + toString(connection->inputPublisherBlocked)); + + if (!connection->outboundQueue.empty()) { + dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", + connection->outboundQueue.size()); + for (DispatchEntry* entry : connection->outboundQueue) { + dump.append(INDENT4); + entry->eventEntry->appendDescription(dump); + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f); + } + } else { + dump += INDENT3 "OutboundQueue: \n"; + } + + if (!connection->waitQueue.empty()) { + dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", + connection->waitQueue.size()); + for (DispatchEntry* entry : connection->waitQueue) { + dump += INDENT4; + entry->eventEntry->appendDescription(dump); + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " + "age=%0.1fms, wait=%0.1fms\n", + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f, + (currentTime - entry->deliveryTime) * 0.000001f); + } + } else { + dump += INDENT3 "WaitQueue: \n"; + } + } + } else { + dump += INDENT "Connections: \n"; + } + + if (isAppSwitchPendingLocked()) { + dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", + (mAppSwitchDueTime - now()) / 1000000.0); + } else { + dump += INDENT "AppSwitch: not pending\n"; + } + + dump += INDENT "Configuration:\n"; + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", + mConfig.keyRepeatTimeout * 0.000001f); +} + +void InputDispatcher::dumpMonitors(std::string& dump, const std::vector& monitors) { + const size_t numMonitors = monitors.size(); + for (size_t i = 0; i < numMonitors; i++) { + const Monitor& monitor = monitors[i]; + const sp& channel = monitor.inputChannel; + dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); + dump += "\n"; + } +} + +status_t InputDispatcher::registerInputChannel(const sp& inputChannel, + int32_t displayId) { +#if DEBUG_REGISTRATION + ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, + inputChannel->getName().c_str(), displayId); +#endif + + { // acquire lock + std::scoped_lock _l(mLock); + sp existingConnection = getConnectionLocked(inputChannel); + if (existingConnection != nullptr) { + ALOGW("Attempted to register already registered input channel '%s'", + inputChannel->getName().c_str()); + return BAD_VALUE; + } + + sp connection = new Connection(inputChannel, false /*monitor*/); + + int fd = inputChannel->getFd(); + mConnectionsByFd[fd] = connection; + mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); + } // release lock + + // Wake the looper because some connections have changed. + mLooper->wake(); + return OK; +} + +status_t InputDispatcher::registerInputMonitor(const sp& inputChannel, + int32_t displayId, bool isGestureMonitor) { + { // acquire lock + std::scoped_lock _l(mLock); + + if (displayId < 0) { + ALOGW("Attempted to register input monitor without a specified display."); + return BAD_VALUE; + } + + if (inputChannel->getToken() == nullptr) { + ALOGW("Attempted to register input monitor without an identifying token."); + return BAD_VALUE; + } + + sp connection = new Connection(inputChannel, true /*monitor*/); + + const int fd = inputChannel->getFd(); + mConnectionsByFd[fd] = connection; + mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + + auto& monitorsByDisplay = + isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; + monitorsByDisplay[displayId].emplace_back(inputChannel); + + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); + } + // Wake the looper because some connections have changed. + mLooper->wake(); + return OK; +} + +status_t InputDispatcher::unregisterInputChannel(const sp& inputChannel) { +#if DEBUG_REGISTRATION + ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str()); +#endif + + { // acquire lock + std::scoped_lock _l(mLock); + + status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/); + if (status) { + return status; + } + } // release lock + + // Wake the poll loop because removing the connection may have changed the current + // synchronization state. + mLooper->wake(); + return OK; +} + +status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, + bool notify) { + sp connection = getConnectionLocked(inputChannel); + if (connection == nullptr) { + ALOGW("Attempted to unregister already unregistered input channel '%s'", + inputChannel->getName().c_str()); + return BAD_VALUE; + } + + const bool removed = removeByValue(mConnectionsByFd, connection); + ALOG_ASSERT(removed); + mInputChannelsByToken.erase(inputChannel->getToken()); + + if (connection->monitor) { + removeMonitorChannelLocked(inputChannel); + } + + mLooper->removeFd(inputChannel->getFd()); + + nsecs_t currentTime = now(); + abortBrokenDispatchCycleLocked(currentTime, connection, notify); + + connection->status = Connection::STATUS_ZOMBIE; + return OK; +} + +void InputDispatcher::removeMonitorChannelLocked(const sp& inputChannel) { + removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay); + removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay); +} + +void InputDispatcher::removeMonitorChannelLocked( + const sp& inputChannel, + std::unordered_map>& monitorsByDisplay) { + for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) { + std::vector& monitors = it->second; + const size_t numMonitors = monitors.size(); + for (size_t i = 0; i < numMonitors; i++) { + if (monitors[i].inputChannel == inputChannel) { + monitors.erase(monitors.begin() + i); + break; + } + } + if (monitors.empty()) { + it = monitorsByDisplay.erase(it); + } else { + ++it; + } + } +} + +status_t InputDispatcher::pilferPointers(const sp& token) { + { // acquire lock + std::scoped_lock _l(mLock); + std::optional foundDisplayId = findGestureMonitorDisplayByTokenLocked(token); + + if (!foundDisplayId) { + ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token"); + return BAD_VALUE; + } + int32_t displayId = foundDisplayId.value(); + + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex < 0) { + ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId); + return BAD_VALUE; + } + + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + std::optional foundDeviceId; + for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { + if (touchedMonitor.monitor.inputChannel->getToken() == token) { + foundDeviceId = state.deviceId; + } + } + if (!foundDeviceId || !state.down) { + ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams." + " Ignoring."); + return BAD_VALUE; + } + int32_t deviceId = foundDeviceId.value(); + + // Send cancel events to all the input channels we're stealing from. + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "gesture monitor stole pointer stream"); + options.deviceId = deviceId; + options.displayId = displayId; + for (const TouchedWindow& window : state.windows) { + sp channel = getInputChannelLocked(window.windowHandle->getToken()); + synthesizeCancelationEventsForInputChannelLocked(channel, options); + } + // Then clear the current touch state so we stop dispatching to them as well. + state.filterNonMonitors(); + } + return OK; +} + +std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( + const sp& token) { + for (const auto& it : mGestureMonitorsByDisplay) { + const std::vector& monitors = it.second; + for (const Monitor& monitor : monitors) { + if (monitor.inputChannel->getToken() == token) { + return it.first; + } + } + } + return std::nullopt; +} + +sp InputDispatcher::getConnectionLocked( + const sp& inputChannel) { + if (inputChannel == nullptr) { + return nullptr; + } + + for (const auto& pair : mConnectionsByFd) { + sp connection = pair.second; + if (connection->inputChannel->getToken() == inputChannel->getToken()) { + return connection; + } + } + + return nullptr; +} + +void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, + const sp& connection, uint32_t seq, + bool handled) { + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); + commandEntry->connection = connection; + commandEntry->eventTime = currentTime; + commandEntry->seq = seq; + commandEntry->handled = handled; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, + const sp& connection) { + ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", + connection->getInputChannelName().c_str()); + + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + commandEntry->connection = connection; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::onFocusChangedLocked(const sp& oldFocus, + const sp& newFocus) { + sp oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; + sp newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; + std::unique_ptr commandEntry = std::make_unique( + &InputDispatcher::doNotifyFocusChangedLockedInterruptible); + commandEntry->oldToken = oldToken; + commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::onANRLocked(nsecs_t currentTime, + const sp& applicationHandle, + const sp& windowHandle, nsecs_t eventTime, + nsecs_t waitStartTime, const char* reason) { + float dispatchLatency = (currentTime - eventTime) * 0.000001f; + float waitDuration = (currentTime - waitStartTime) * 0.000001f; + ALOGI("Application is not responding: %s. " + "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, + waitDuration, reason); + + // Capture a record of the InputDispatcher state at the time of the ANR. + time_t t = time(nullptr); + struct tm tm; + localtime_r(&t, &tm); + char timestr[64]; + strftime(timestr, sizeof(timestr), "%F %T", &tm); + mLastANRState.clear(); + mLastANRState += INDENT "ANR:\n"; + mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr); + mLastANRState += + StringPrintf(INDENT2 "Window: %s\n", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); + mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); + mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); + mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); + dumpDispatchStateLocked(mLastANRState); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyANRLockedInterruptible); + commandEntry->inputApplicationHandle = applicationHandle; + commandEntry->inputChannel = + windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; + commandEntry->reason = reason; + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyConfigurationChanged(commandEntry->eventTime); + + mLock.lock(); +} + +void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) { + sp connection = commandEntry->connection; + + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); + + mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken()); + + mLock.lock(); + } +} + +void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) { + sp oldToken = commandEntry->oldToken; + sp newToken = commandEntry->newToken; + mLock.unlock(); + mPolicy->notifyFocusChanged(oldToken, newToken); + mLock.lock(); +} + +void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + nsecs_t newTimeout = + mPolicy->notifyANR(commandEntry->inputApplicationHandle, + commandEntry->inputChannel ? commandEntry->inputChannel->getToken() + : nullptr, + commandEntry->reason); + + mLock.lock(); + + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); +} + +void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( + CommandEntry* commandEntry) { + KeyEntry* entry = commandEntry->keyEntry; + + KeyEvent event; + initializeKeyEvent(&event, entry); + + mLock.unlock(); + + android::base::Timer t; + sp token = commandEntry->inputChannel != nullptr + ? commandEntry->inputChannel->getToken() + : nullptr; + nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + + mLock.lock(); + + if (delay < 0) { + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; + } else if (!delay) { + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + } else { + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; + entry->interceptKeyWakeupTime = now() + delay; + } + entry->release(); +} + +void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + mPolicy->onPointerDownOutsideFocus(commandEntry->newToken); + mLock.lock(); +} + +void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { + sp connection = commandEntry->connection; + const nsecs_t finishTime = commandEntry->eventTime; + uint32_t seq = commandEntry->seq; + const bool handled = commandEntry->handled; + + // Handle post-event policy actions. + std::deque::iterator dispatchEntryIt = + connection->findWaitQueueEntry(seq); + if (dispatchEntryIt == connection->waitQueue.end()) { + return; + } + DispatchEntry* dispatchEntry = *dispatchEntryIt; + + nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { + std::string msg = + StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", + connection->getWindowName().c_str(), eventDuration * 0.000001f); + dispatchEntry->eventEntry->appendDescription(msg); + ALOGI("%s", msg.c_str()); + } + + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = + afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, + handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Note that because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt != connection->waitQueue.end()) { + dispatchEntry = *dispatchEntryIt; + connection->waitQueue.erase(dispatchEntryIt); + traceWaitQueueLength(connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.push_front(dispatchEntry); + traceOutboundQueueLength(connection); + } else { + releaseDispatchEntry(dispatchEntry); + } + } + + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); +} + +bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& connection, + DispatchEntry* dispatchEntry, + KeyEntry* keyEntry, bool handled) { + if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) { + if (!handled) { + // Report the key as unhandled, since the fallback was not handled. + mReporter->reportUnhandledKey(keyEntry->sequenceNum); + } + return false; + } + + // Get the fallback key state. + // Clear it out after dispatching the UP. + int32_t originalKeyCode = keyEntry->keyCode; + int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode); + if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + connection->inputState.removeFallbackKey(originalKeyCode); + } + + if (handled || !dispatchEntry->hasForegroundTarget()) { + // If the application handles the original key for which we previously + // generated a fallback or if the window is not a foreground window, + // then cancel the associated fallback key, if any. + if (fallbackKeyCode != -1) { + // Dispatch the unhandled key to the policy with the cancel flag. +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: Asking policy to cancel fallback action. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, + keyEntry->policyFlags); +#endif + KeyEvent event; + initializeKeyEvent(&event, keyEntry); + event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); + + mLock.unlock(); + + mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + keyEntry->policyFlags, &event); + + mLock.lock(); + + // Cancel the fallback key. + if (fallbackKeyCode != AKEYCODE_UNKNOWN) { + CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + "application handled the original non-fallback key " + "or is no longer a foreground target, " + "canceling previously dispatched fallback key"); + options.keyCode = fallbackKeyCode; + synthesizeCancelationEventsForConnectionLocked(connection, options); + } + connection->inputState.removeFallbackKey(originalKeyCode); + } + } else { + // If the application did not handle a non-fallback key, first check + // that we are in a good state to perform unhandled key event processing + // Then ask the policy what to do with it. + bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0; + if (fallbackKeyCode == -1 && !initialDown) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: Skipping unhandled key event processing " + "since this is not an initial down. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); +#endif + return false; + } + + // Dispatch the unhandled key to the policy. +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: Asking policy to perform fallback action. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); +#endif + KeyEvent event; + initializeKeyEvent(&event, keyEntry); + + mLock.unlock(); + + bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + keyEntry->policyFlags, &event); + + mLock.lock(); + + if (connection->status != Connection::STATUS_NORMAL) { + connection->inputState.removeFallbackKey(originalKeyCode); + return false; + } + + // Latch the fallback keycode for this key on an initial down. + // The fallback keycode cannot change at any other point in the lifecycle. + if (initialDown) { + if (fallback) { + fallbackKeyCode = event.getKeyCode(); + } else { + fallbackKeyCode = AKEYCODE_UNKNOWN; + } + connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode); + } + + ALOG_ASSERT(fallbackKeyCode != -1); + + // Cancel the fallback key if the policy decides not to send it anymore. + // We will continue to dispatch the key to the policy but we will no + // longer dispatch a fallback key to the application. + if (fallbackKeyCode != AKEYCODE_UNKNOWN && + (!fallback || fallbackKeyCode != event.getKeyCode())) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + if (fallback) { + ALOGD("Unhandled key event: Policy requested to send key %d" + "as a fallback for %d, but on the DOWN it had requested " + "to send %d instead. Fallback canceled.", + event.getKeyCode(), originalKeyCode, fallbackKeyCode); + } else { + ALOGD("Unhandled key event: Policy did not request fallback for %d, " + "but on the DOWN it had requested to send %d. " + "Fallback canceled.", + originalKeyCode, fallbackKeyCode); + } +#endif + + CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + "canceling fallback, policy no longer desires it"); + options.keyCode = fallbackKeyCode; + synthesizeCancelationEventsForConnectionLocked(connection, options); + + fallback = false; + fallbackKeyCode = AKEYCODE_UNKNOWN; + if (keyEntry->action != AKEY_EVENT_ACTION_UP) { + connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode); + } + } + +#if DEBUG_OUTBOUND_EVENT_DETAILS + { + std::string msg; + const KeyedVector& fallbackKeys = + connection->inputState.getFallbackKeys(); + for (size_t i = 0; i < fallbackKeys.size(); i++) { + msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i)); + } + ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.", + fallbackKeys.size(), msg.c_str()); + } +#endif + + if (fallback) { + // Restart the dispatch cycle using the fallback key. + keyEntry->eventTime = event.getEventTime(); + keyEntry->deviceId = event.getDeviceId(); + keyEntry->source = event.getSource(); + keyEntry->displayId = event.getDisplayId(); + keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; + keyEntry->keyCode = fallbackKeyCode; + keyEntry->scanCode = event.getScanCode(); + keyEntry->metaState = event.getMetaState(); + keyEntry->repeatCount = event.getRepeatCount(); + keyEntry->downTime = event.getDownTime(); + keyEntry->syntheticRepeat = false; + +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: Dispatching fallback key. " + "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", + originalKeyCode, fallbackKeyCode, keyEntry->metaState); +#endif + return true; // restart the event + } else { +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: No fallback key."); +#endif + + // Report the key as unhandled, since there is no fallback key. + mReporter->reportUnhandledKey(keyEntry->sequenceNum); + } + } + return false; +} + +bool InputDispatcher::afterMotionEventLockedInterruptible(const sp& connection, + DispatchEntry* dispatchEntry, + MotionEntry* motionEntry, bool handled) { + return false; +} + +void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType); + + mLock.lock(); +} + +void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) { + event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, + entry->downTime, entry->eventTime); +} + +void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, + int32_t injectionResult, + nsecs_t timeSpentWaitingForApplication) { + // TODO Write some statistics about how long we spend waiting. +} + +void InputDispatcher::traceInboundQueueLengthLocked() { + if (ATRACE_ENABLED()) { + ATRACE_INT("iq", mInboundQueue.size()); + } +} + +void InputDispatcher::traceOutboundQueueLength(const sp& connection) { + if (ATRACE_ENABLED()) { + char counterName[40]; + snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); + ATRACE_INT(counterName, connection->outboundQueue.size()); + } +} + +void InputDispatcher::traceWaitQueueLength(const sp& connection) { + if (ATRACE_ENABLED()) { + char counterName[40]; + snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); + ATRACE_INT(counterName, connection->waitQueue.size()); + } +} + +void InputDispatcher::dump(std::string& dump) { + std::scoped_lock _l(mLock); + + dump += "Input Dispatcher State:\n"; + dumpDispatchStateLocked(dump); + + if (!mLastANRState.empty()) { + dump += "\nInput Dispatcher State at time of last ANR:\n"; + dump += mLastANRState; + } +} + +void InputDispatcher::monitor() { + // Acquire and release the lock to ensure that the dispatcher has not deadlocked. + std::unique_lock _l(mLock); + mLooper->wake(); + mDispatcherIsAlive.wait(_l); +} + +// --- InputDispatcher::InjectionState --- + +InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) + : refCount(1), + injectorPid(injectorPid), + injectorUid(injectorUid), + injectionResult(INPUT_EVENT_INJECTION_PENDING), + injectionIsAsync(false), + pendingForegroundDispatches(0) {} + +InputDispatcher::InjectionState::~InjectionState() {} + +void InputDispatcher::InjectionState::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +// --- InputDispatcher::EventEntry --- + +InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, + uint32_t policyFlags) + : sequenceNum(sequenceNum), + refCount(1), + type(type), + eventTime(eventTime), + policyFlags(policyFlags), + injectionState(nullptr), + dispatchInProgress(false) {} + +InputDispatcher::EventEntry::~EventEntry() { + releaseInjectionState(); +} + +void InputDispatcher::EventEntry::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +void InputDispatcher::EventEntry::releaseInjectionState() { + if (injectionState) { + injectionState->release(); + injectionState = nullptr; + } +} + +// --- InputDispatcher::ConfigurationChangedEntry --- + +InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, + nsecs_t eventTime) + : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {} + +InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {} + +void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); +} + +// --- InputDispatcher::DeviceResetEntry --- + +InputDispatcher::DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, + int32_t deviceId) + : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} + +InputDispatcher::DeviceResetEntry::~DeviceResetEntry() {} + +void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); +} + +// --- InputDispatcher::KeyEntry --- + +InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, + uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime) + : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + flags(flags), + keyCode(keyCode), + scanCode(scanCode), + metaState(metaState), + repeatCount(repeatCount), + downTime(downTime), + syntheticRepeat(false), + interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), + interceptKeyWakeupTime(0) {} + +InputDispatcher::KeyEntry::~KeyEntry() {} + +void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " + "repeatCount=%d), policyFlags=0x%08x", + deviceId, source, displayId, keyActionToString(action).c_str(), flags, + keyCode, scanCode, metaState, repeatCount, policyFlags); +} + +void InputDispatcher::KeyEntry::recycle() { + releaseInjectionState(); + + dispatchInProgress = false; + syntheticRepeat = false; + interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + interceptKeyWakeupTime = 0; +} + +// --- InputDispatcher::MotionEntry --- + +InputDispatcher::MotionEntry::MotionEntry( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset) + : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + eventTime(eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), + pointerCount(pointerCount) { + for (uint32_t i = 0; i < pointerCount; i++) { + this->pointerProperties[i].copyFrom(pointerProperties[i]); + this->pointerCoords[i].copyFrom(pointerCoords[i]); + if (xOffset || yOffset) { + this->pointerCoords[i].applyOffset(xOffset, yOffset); + } + } +} + +InputDispatcher::MotionEntry::~MotionEntry() {} + +void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " + "buttonState=0x%08x, " + "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " + "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), + actionButton, flags, metaState, buttonState, + motionClassificationToString(classification), edgeFlags, xPrecision, + yPrecision, xCursorPosition, yCursorPosition); + + for (uint32_t i = 0; i < pointerCount; i++) { + if (i) { + msg += ", "; + } + msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(), + pointerCoords[i].getY()); + } + msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); +} + +// --- InputDispatcher::DispatchEntry --- + +volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; + +InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, + float xOffset, float yOffset, float globalScaleFactor, + float windowXScale, float windowYScale) + : seq(nextSeq()), + eventEntry(eventEntry), + targetFlags(targetFlags), + xOffset(xOffset), + yOffset(yOffset), + globalScaleFactor(globalScaleFactor), + windowXScale(windowXScale), + windowYScale(windowYScale), + deliveryTime(0), + resolvedAction(0), + resolvedFlags(0) { + eventEntry->refCount += 1; +} + +InputDispatcher::DispatchEntry::~DispatchEntry() { + eventEntry->release(); +} + +uint32_t InputDispatcher::DispatchEntry::nextSeq() { + // Sequence number 0 is reserved and will never be returned. + uint32_t seq; + do { + seq = android_atomic_inc(&sNextSeqAtomic); + } while (!seq); + return seq; +} + +// --- InputDispatcher::InputState --- + +InputDispatcher::InputState::InputState() {} + +InputDispatcher::InputState::~InputState() {} + +bool InputDispatcher::InputState::isNeutral() const { + return mKeyMementos.empty() && mMotionMementos.empty(); +} + +bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source, + int32_t displayId) const { + for (const MotionMemento& memento : mMotionMementos) { + if (memento.deviceId == deviceId && memento.source == source && + memento.displayId == displayId && memento.hovering) { + return true; + } + } + return false; +} + +bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) { + switch (action) { + case AKEY_EVENT_ACTION_UP: { + if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + for (size_t i = 0; i < mFallbackKeys.size();) { + if (mFallbackKeys.valueAt(i) == entry->keyCode) { + mFallbackKeys.removeItemsAt(i); + } else { + i += 1; + } + } + } + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + return true; + } + /* FIXME: We can't just drop the key up event because that prevents creating + * popup windows that are automatically shown when a key is held and then + * dismissed when the key is released. The problem is that the popup will + * not have received the original key down, so the key up will be considered + * to be inconsistent with its observed state. We could perhaps handle this + * by synthesizing a key down but that will cause other problems. + * + * So for now, allow inconsistent key up events to be dispatched. + * + #if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " + "keyCode=%d, scanCode=%d", + entry->deviceId, entry->source, entry->keyCode, entry->scanCode); + #endif + return false; + */ + return true; + } + + case AKEY_EVENT_ACTION_DOWN: { + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + } + addKeyMemento(entry, flags); + return true; + } + + default: + return true; + } +} + +bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action, + int32_t flags) { + int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; + switch (actionMasked) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " + "displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, false /*hovering*/); + return true; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_MOVE: { + if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { + // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need + // to generate cancellation events for these since they're based in relative rather + // than absolute units. + return true; + } + + ssize_t index = findMotionMemento(entry, false /*hovering*/); + + if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { + // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all + // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. + // Any other value and we need to track the motion so we can send cancellation + // events for anything generating fallback events (e.g. DPad keys for joystick + // movements). + if (index >= 0) { + if (entry->pointerCoords[0].isEmpty()) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } else { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + } + } else if (!entry->pointerCoords[0].isEmpty()) { + addMotionMemento(entry, flags, false /*hovering*/); + } + + // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. + return true; + } + if (index >= 0) { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion pointer up/down or move event: " + "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " + "displayId=%" PRId32, + entry->deviceId, entry->source, entry->displayId); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, true /*hovering*/); + return true; + } + + default: + return true; + } +} + +ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && + memento.scanCode == entry->scanCode) { + return i; + } + } + return -1; +} + +ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, + bool hovering) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.hovering == hovering) { + return i; + } + } + return -1; +} + +void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { + KeyMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.metaState = entry->metaState; + memento.flags = flags; + memento.downTime = entry->downTime; + memento.policyFlags = entry->policyFlags; + mKeyMementos.push_back(memento); +} + +void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, + bool hovering) { + MotionMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.flags = flags; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.xCursorPosition = entry->xCursorPosition; + memento.yCursorPosition = entry->yCursorPosition; + memento.downTime = entry->downTime; + memento.setPointers(entry); + memento.hovering = hovering; + memento.policyFlags = entry->policyFlags; + mMotionMementos.push_back(memento); +} + +void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { + pointerCount = entry->pointerCount; + for (uint32_t i = 0; i < entry->pointerCount; i++) { + pointerProperties[i].copyFrom(entry->pointerProperties[i]); + pointerCoords[i].copyFrom(entry->pointerCoords[i]); + } +} + +void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, + std::vector& outEvents, + const CancelationOptions& options) { + for (KeyMemento& memento : mKeyMementos) { + if (shouldCancelKey(memento, options)) { + outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, AKEY_EVENT_ACTION_UP, + memento.flags | AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, memento.metaState, + 0, memento.downTime)); + } + } + + for (const MotionMemento& memento : mMotionMementos) { + if (shouldCancelMotion(memento, options)) { + const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT + : AMOTION_EVENT_ACTION_CANCEL; + outEvents.push_back( + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, memento.pointerCount, + memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); + } + } +} + +void InputDispatcher::InputState::clear() { + mKeyMementos.clear(); + mMotionMementos.clear(); + mFallbackKeys.clear(); +} + +void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { + for (size_t j = 0; j < other.mMotionMementos.size();) { + const MotionMemento& otherMemento = other.mMotionMementos[j]; + if (memento.deviceId == otherMemento.deviceId && + memento.source == otherMemento.source && + memento.displayId == otherMemento.displayId) { + other.mMotionMementos.erase(other.mMotionMementos.begin() + j); + } else { + j += 1; + } + } + other.mMotionMementos.push_back(memento); + } + } +} + +int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + return index >= 0 ? mFallbackKeys.valueAt(index) : -1; +} + +void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + if (index >= 0) { + mFallbackKeys.replaceValueAt(index, fallbackKeyCode); + } else { + mFallbackKeys.add(originalKeyCode, fallbackKeyCode); + } +} + +void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) { + mFallbackKeys.removeItem(originalKeyCode); +} + +bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, + const CancelationOptions& options) { + if (options.keyCode && memento.keyCode != options.keyCode.value()) { + return false; + } + + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return true; + case CancelationOptions::CANCEL_FALLBACK_EVENTS: + return memento.flags & AKEY_EVENT_FLAG_FALLBACK; + default: + return false; + } +} + +bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento, + const CancelationOptions& options) { + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + return true; + case CancelationOptions::CANCEL_POINTER_EVENTS: + return memento.source & AINPUT_SOURCE_CLASS_POINTER; + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + default: + return false; + } +} + +// --- InputDispatcher::Connection --- + +InputDispatcher::Connection::Connection(const sp& inputChannel, bool monitor) + : status(STATUS_NORMAL), + inputChannel(inputChannel), + monitor(monitor), + inputPublisher(inputChannel), + inputPublisherBlocked(false) {} + +InputDispatcher::Connection::~Connection() {} + +const std::string InputDispatcher::Connection::getWindowName() const { + if (inputChannel != nullptr) { + return inputChannel->getName(); + } + if (monitor) { + return "monitor"; + } + return "?"; +} + +const char* InputDispatcher::Connection::getStatusLabel() const { + switch (status) { + case STATUS_NORMAL: + return "NORMAL"; + + case STATUS_BROKEN: + return "BROKEN"; + + case STATUS_ZOMBIE: + return "ZOMBIE"; + + default: + return "UNKNOWN"; + } +} + +std::deque::iterator +InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { + for (std::deque::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { + if ((*it)->seq == seq) { + return it; + } + } + return waitQueue.end(); +} + +// --- InputDispatcher::Monitor +InputDispatcher::Monitor::Monitor(const sp& inputChannel) + : inputChannel(inputChannel) {} + +// --- InputDispatcher::CommandEntry --- +// +InputDispatcher::CommandEntry::CommandEntry(Command command) + : command(command), + eventTime(0), + keyEntry(nullptr), + userActivityEventType(0), + seq(0), + handled(false) {} + +InputDispatcher::CommandEntry::~CommandEntry() {} + +// --- InputDispatcher::TouchedMonitor --- +InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, + float yOffset) + : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {} + +// --- InputDispatcher::TouchState --- + +InputDispatcher::TouchState::TouchState() + : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {} + +InputDispatcher::TouchState::~TouchState() {} + +void InputDispatcher::TouchState::reset() { + down = false; + split = false; + deviceId = -1; + source = 0; + displayId = ADISPLAY_ID_NONE; + windows.clear(); + portalWindows.clear(); + gestureMonitors.clear(); +} + +void InputDispatcher::TouchState::copyFrom(const TouchState& other) { + down = other.down; + split = other.split; + deviceId = other.deviceId; + source = other.source; + displayId = other.displayId; + windows = other.windows; + portalWindows = other.portalWindows; + gestureMonitors = other.gestureMonitors; +} + +void InputDispatcher::TouchState::addOrUpdateWindow(const sp& windowHandle, + int32_t targetFlags, BitSet32 pointerIds) { + if (targetFlags & InputTarget::FLAG_SPLIT) { + split = true; + } + + for (size_t i = 0; i < windows.size(); i++) { + TouchedWindow& touchedWindow = windows[i]; + if (touchedWindow.windowHandle == windowHandle) { + touchedWindow.targetFlags |= targetFlags; + if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; + } + touchedWindow.pointerIds.value |= pointerIds.value; + return; + } + } + + TouchedWindow touchedWindow; + touchedWindow.windowHandle = windowHandle; + touchedWindow.targetFlags = targetFlags; + touchedWindow.pointerIds = pointerIds; + windows.push_back(touchedWindow); +} + +void InputDispatcher::TouchState::addPortalWindow(const sp& windowHandle) { + size_t numWindows = portalWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + if (portalWindows[i] == windowHandle) { + return; + } + } + portalWindows.push_back(windowHandle); +} + +void InputDispatcher::TouchState::addGestureMonitors( + const std::vector& newMonitors) { + const size_t newSize = gestureMonitors.size() + newMonitors.size(); + gestureMonitors.reserve(newSize); + gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors), + std::end(newMonitors)); +} + +void InputDispatcher::TouchState::removeWindow(const sp& windowHandle) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle == windowHandle) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void InputDispatcher::TouchState::removeWindowByToken(const sp& token) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle->getToken() == token) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { + for (size_t i = 0; i < windows.size();) { + TouchedWindow& window = windows[i]; + if (window.targetFlags & + (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { + window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; + window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; + i += 1; + } else { + windows.erase(windows.begin() + i); + } + } +} + +void InputDispatcher::TouchState::filterNonMonitors() { + windows.clear(); + portalWindows.clear(); +} + +sp InputDispatcher::TouchState::getFirstForegroundWindowHandle() const { + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& window = windows[i]; + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + return window.windowHandle; + } + } + return nullptr; +} + +bool InputDispatcher::TouchState::isSlippery() const { + // Must have exactly one foreground window. + bool haveSlipperyForegroundWindow = false; + for (const TouchedWindow& window : windows) { + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (haveSlipperyForegroundWindow || + !(window.windowHandle->getInfo()->layoutParamsFlags & + InputWindowInfo::FLAG_SLIPPERY)) { + return false; + } + haveSlipperyForegroundWindow = true; + } + } + return haveSlipperyForegroundWindow; +} + +// --- InputDispatcherThread --- + +InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) + : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {} + +InputDispatcherThread::~InputDispatcherThread() {} + +bool InputDispatcherThread::threadLoop() { + mDispatcher->dispatchOnce(); + return true; +} + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h new file mode 100644 index 0000000000..a90f958e79 --- /dev/null +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -0,0 +1,1227 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_DISPATCHER_H +#define _UI_INPUT_DISPATCHER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "InputListener.h" +#include "InputReporterInterface.h" + +namespace android { + +/* + * Constants used to report the outcome of input event injection. + */ +enum { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + INPUT_EVENT_INJECTION_PENDING = -1, + + /* Injection succeeded. */ + INPUT_EVENT_INJECTION_SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + INPUT_EVENT_INJECTION_FAILED = 2, + + /* Injection failed due to a timeout. */ + INPUT_EVENT_INJECTION_TIMED_OUT = 3 +}; + +/* + * Constants used to determine the input event injection synchronization mode. + */ +enum { + /* Injection is asynchronous and is assumed always to be successful. */ + INPUT_EVENT_INJECTION_SYNC_NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, +}; + +/* + * An input target specifies how an input event is to be dispatched to a particular window + * including the window's input channel, control flags, a timeout, and an X / Y offset to + * be added to input event coordinates to compensate for the absolute position of the + * window area. + */ +struct InputTarget { + enum { + /* This flag indicates that the event is being delivered to a foreground application. */ + FLAG_FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + FLAG_WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + FLAG_SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + FLAG_ZERO_COORDS = 1 << 3, + + /* This flag indicates that the event should be sent as is. + * Should always be set unless the event is to be transmuted. */ + FLAG_DISPATCH_AS_IS = 1 << 8, + + /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside + * of the area of this target and so should instead be delivered as an + * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ + FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + + /* This flag indicates that a hover sequence is starting in the given window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + + /* This flag indicates that a hover event happened outside of a window which handled + * previous hover events, signifying the end of the current hover sequence for that + * window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + + /* This flag indicates that the event should be canceled. + * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips + * outside of a window. */ + FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, + + /* This flag indicates that the event should be dispatched as an initial down. + * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips + * into a new window. */ + FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, + + /* Mask for all dispatch modes. */ + FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | + FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | + FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, + + }; + + // The input channel to be targeted. + sp inputChannel; + + // Flags for the input target. + int32_t flags; + + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset, yOffset; + + // Scaling factor to apply to MotionEvent as it is delivered. + // (ignored for KeyEvents) + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + + // The subset of pointer ids to include in motion events dispatched to this input target + // if FLAG_SPLIT is set. + BitSet32 pointerIds; +}; + +/* + * Input dispatcher configuration. + * + * Specifies various options that modify the behavior of the input dispatcher. + * The values provided here are merely defaults. The actual values will come from ViewConfiguration + * and are passed into the dispatcher during initialization. + */ +struct InputDispatcherConfiguration { + // The key repeat initial timeout. + nsecs_t keyRepeatTimeout; + + // The key repeat inter-key delay. + nsecs_t keyRepeatDelay; + + InputDispatcherConfiguration() + : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} +}; + +/* + * Input dispatcher policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputDispatcherPolicyInterface : public virtual RefBase { +protected: + InputDispatcherPolicyInterface() {} + virtual ~InputDispatcherPolicyInterface() {} + +public: + /* Notifies the system that a configuration change has occurred. */ + virtual void notifyConfigurationChanged(nsecs_t when) = 0; + + /* Notifies the system that an application is not responding. + * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ + virtual nsecs_t notifyANR(const sp& inputApplicationHandle, + const sp& token, const std::string& reason) = 0; + + /* Notifies the system that an input channel is unrecoverably broken. */ + virtual void notifyInputChannelBroken(const sp& token) = 0; + virtual void notifyFocusChanged(const sp& oldToken, const sp& newToken) = 0; + + /* Gets the input dispatcher configuration. */ + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; + + /* Filters an input event. + * Return true to dispatch the event unmodified, false to consume the event. + * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED + * to injectInputEvent. + */ + virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; + + /* Intercepts a key event immediately before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; + + /* Intercepts a touch, trackball or other motion event before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) = 0; + + /* Allows the policy a chance to intercept a key before dispatching. */ + virtual nsecs_t interceptKeyBeforeDispatching(const sp& token, + const KeyEvent* keyEvent, + uint32_t policyFlags) = 0; + + /* Allows the policy a chance to perform default processing for an unhandled key. + * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ + virtual bool dispatchUnhandledKey(const sp& token, const KeyEvent* keyEvent, + uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; + + /* Notifies the policy about switch events. + */ + virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) = 0; + + /* Poke user activity for an event dispatched to a window. */ + virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; + + /* Checks whether a given application pid/uid has permission to inject input events + * into other applications. + * + * This method is special in that its implementation promises to be non-reentrant and + * is safe to call while holding other locks. (Most other methods make no such guarantees!) + */ + virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, + int32_t injectorUid) = 0; + + /* Notifies the policy that a pointer down event has occurred outside the current focused + * window. + * + * The touchedToken passed as an argument is the window that received the input event. + */ + virtual void onPointerDownOutsideFocus(const sp& touchedToken) = 0; +}; + +/* Notifies the system about input events generated by the input reader. + * The dispatcher is expected to be mostly asynchronous. */ +class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { +protected: + InputDispatcherInterface() {} + virtual ~InputDispatcherInterface() {} + +public: + /* Dumps the state of the input dispatcher. + * + * This method may be called on any thread (usually by the input manager). */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + /* Runs a single iteration of the dispatch loop. + * Nominally processes one queued event, a timeout, or a response from an input consumer. + * + * This method should only be called on the input dispatcher thread. + */ + virtual void dispatchOnce() = 0; + + /* Injects an input event and optionally waits for sync. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + uint32_t policyFlags) = 0; + + /* Sets the list of input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputWindows( + const std::vector>& inputWindowHandles, int32_t displayId, + const sp& setInputWindowsListener = nullptr) = 0; + + /* Sets the focused application on the given display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedApplication( + int32_t displayId, const sp& inputApplicationHandle) = 0; + + /* Sets the focused display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedDisplay(int32_t displayId) = 0; + + /* Sets the input dispatching mode. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; + + /* Sets whether input event filtering is enabled. + * When enabled, incoming input events are sent to the policy's filterInputEvent + * method instead of being dispatched. The filter is expected to use + * injectInputEvent to inject the events it would like to have dispatched. + * It should include POLICY_FLAG_FILTERED in the policy flags during injection. + */ + virtual void setInputFilterEnabled(bool enabled) = 0; + + /* Transfers touch focus from one window to another window. + * + * Returns true on success. False if the window did not actually have touch focus. + */ + virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; + + /* Registers input channels that may be used as targets for input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputChannel(const sp& inputChannel, + int32_t displayId) = 0; + + /* Registers input channels to be used to monitor input events. + * + * Each monitor must target a specific display and will only receive input events sent to that + * display. If the monitor is a gesture monitor, it will only receive pointer events on the + * targeted display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputMonitor(const sp& inputChannel, int32_t displayId, + bool gestureMonitor) = 0; + + /* Unregister input channels that will no longer receive input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; + + /* Allows an input monitor steal the current pointer stream away from normal input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t pilferPointers(const sp& token) = 0; +}; + +/* Dispatches events to input targets. Some functions of the input dispatcher, such as + * identifying input targets, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy can potentially block or cause re-entrance into the input dispatcher, + * the input dispatcher never calls into the policy while holding its internal locks. + * The implementation is also carefully designed to recover from scenarios such as an + * input channel becoming unregistered while identifying input targets or processing timeouts. + * + * Methods marked 'Locked' must be called with the lock acquired. + * + * Methods marked 'LockedInterruptible' must be called with the lock acquired but + * may during the course of their execution release the lock, call into the policy, and + * then reacquire the lock. The caller is responsible for recovering gracefully. + * + * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. + */ +class InputDispatcher : public InputDispatcherInterface { +protected: + virtual ~InputDispatcher(); + +public: + explicit InputDispatcher(const sp& policy); + + virtual void dump(std::string& dump) override; + virtual void monitor() override; + + virtual void dispatchOnce() override; + + virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + virtual void notifyKey(const NotifyKeyArgs* args) override; + virtual void notifyMotion(const NotifyMotionArgs* args) override; + virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + uint32_t policyFlags) override; + + virtual void setInputWindows( + const std::vector>& inputWindowHandles, int32_t displayId, + const sp& setInputWindowsListener = nullptr) override; + virtual void setFocusedApplication( + int32_t displayId, const sp& inputApplicationHandle) override; + virtual void setFocusedDisplay(int32_t displayId) override; + virtual void setInputDispatchMode(bool enabled, bool frozen) override; + virtual void setInputFilterEnabled(bool enabled) override; + + virtual bool transferTouchFocus(const sp& fromToken, + const sp& toToken) override; + + virtual status_t registerInputChannel(const sp& inputChannel, + int32_t displayId) override; + virtual status_t registerInputMonitor(const sp& inputChannel, int32_t displayId, + bool isGestureMonitor) override; + virtual status_t unregisterInputChannel(const sp& inputChannel) override; + virtual status_t pilferPointers(const sp& token) override; + +private: + struct InjectionState { + mutable int32_t refCount; + + int32_t injectorPid; + int32_t injectorUid; + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress + + InjectionState(int32_t injectorPid, int32_t injectorUid); + void release(); + + private: + ~InjectionState(); + }; + + struct EventEntry { + enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; + + uint32_t sequenceNum; + mutable int32_t refCount; + int32_t type; + nsecs_t eventTime; + uint32_t policyFlags; + InjectionState* injectionState; + + bool dispatchInProgress; // initially false, set to true while dispatching + + inline bool isInjected() const { return injectionState != nullptr; } + + void release(); + + virtual void appendDescription(std::string& msg) const = 0; + + protected: + EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); + virtual ~EventEntry(); + void releaseInjectionState(); + }; + + struct ConfigurationChangedEntry : EventEntry { + explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); + virtual void appendDescription(std::string& msg) const; + + protected: + virtual ~ConfigurationChangedEntry(); + }; + + struct DeviceResetEntry : EventEntry { + int32_t deviceId; + + DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); + virtual void appendDescription(std::string& msg) const; + + protected: + virtual ~DeviceResetEntry(); + }; + + struct KeyEntry : EventEntry { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + + bool syntheticRepeat; // set to true for synthetic key repeats + + enum InterceptKeyResult { + INTERCEPT_KEY_RESULT_UNKNOWN, + INTERCEPT_KEY_RESULT_SKIP, + INTERCEPT_KEY_RESULT_CONTINUE, + INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, + }; + InterceptKeyResult interceptKeyResult; // set based on the interception result + nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER + + KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime); + virtual void appendDescription(std::string& msg) const; + void recycle(); + + protected: + virtual ~KeyEntry(); + }; + + struct MotionEntry : EventEntry { + nsecs_t eventTime; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + + MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); + virtual void appendDescription(std::string& msg) const; + + protected: + virtual ~MotionEntry(); + }; + + // Tracks the progress of dispatching a particular event to a particular connection. + struct DispatchEntry { + const uint32_t seq; // unique sequence number, never 0 + + EventEntry* eventEntry; // the event to dispatch + int32_t targetFlags; + float xOffset; + float yOffset; + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + nsecs_t deliveryTime; // time when the event was actually delivered + + // Set to the resolved action and flags when the event is enqueued. + int32_t resolvedAction; + int32_t resolvedFlags; + + DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, + float globalScaleFactor, float windowXScale, float windowYScale); + ~DispatchEntry(); + + inline bool hasForegroundTarget() const { + return targetFlags & InputTarget::FLAG_FOREGROUND; + } + + inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + + private: + static volatile int32_t sNextSeqAtomic; + + static uint32_t nextSeq(); + }; + + // A command entry captures state and behavior for an action to be performed in the + // dispatch loop after the initial processing has taken place. It is essentially + // a kind of continuation used to postpone sensitive policy interactions to a point + // in the dispatch loop where it is safe to release the lock (generally after finishing + // the critical parts of the dispatch cycle). + // + // The special thing about commands is that they can voluntarily release and reacquire + // the dispatcher lock at will. Initially when the command starts running, the + // dispatcher lock is held. However, if the command needs to call into the policy to + // do some work, it can release the lock, do the work, then reacquire the lock again + // before returning. + // + // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch + // never calls into the policy while holding its lock. + // + // Commands are implicitly 'LockedInterruptible'. + struct CommandEntry; + typedef std::function Command; + + class Connection; + struct CommandEntry { + explicit CommandEntry(Command command); + ~CommandEntry(); + + Command command; + + // parameters for the command (usage varies by command) + sp connection; + nsecs_t eventTime; + KeyEntry* keyEntry; + sp inputApplicationHandle; + std::string reason; + int32_t userActivityEventType; + uint32_t seq; + bool handled; + sp inputChannel; + sp oldToken; + sp newToken; + }; + + /* Specifies which events are to be canceled and why. */ + struct CancelationOptions { + enum Mode { + CANCEL_ALL_EVENTS = 0, + CANCEL_POINTER_EVENTS = 1, + CANCEL_NON_POINTER_EVENTS = 2, + CANCEL_FALLBACK_EVENTS = 3, + }; + + // The criterion to use to determine which events should be canceled. + Mode mode; + + // Descriptive reason for the cancelation. + const char* reason; + + // The specific keycode of the key event to cancel, or nullopt to cancel any key event. + std::optional keyCode = std::nullopt; + + // The specific device id of events to cancel, or nullopt to cancel events from any device. + std::optional deviceId = std::nullopt; + + // The specific display id of events to cancel, or nullopt to cancel events on any display. + std::optional displayId = std::nullopt; + + CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} + }; + + /* Tracks dispatched key and motion event state so that cancelation events can be + * synthesized when events are dropped. */ + class InputState { + public: + InputState(); + ~InputState(); + + // Returns true if there is no state to be canceled. + bool isNeutral() const; + + // Returns true if the specified source is known to have received a hover enter + // motion event. + bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; + + // Records tracking information for a key event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); + + // Records tracking information for a motion event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); + + // Synthesizes cancelation events for the current state and resets the tracked state. + void synthesizeCancelationEvents(nsecs_t currentTime, std::vector& outEvents, + const CancelationOptions& options); + + // Clears the current state. + void clear(); + + // Copies pointer-related parts of the input state to another instance. + void copyPointerStateTo(InputState& other) const; + + // Gets the fallback key associated with a keycode. + // Returns -1 if none. + // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. + int32_t getFallbackKey(int32_t originalKeyCode); + + // Sets the fallback key for a particular keycode. + void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); + + // Removes the fallback key for a particular keycode. + void removeFallbackKey(int32_t originalKeyCode); + + inline const KeyedVector& getFallbackKeys() const { + return mFallbackKeys; + } + + private: + struct KeyMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t flags; + nsecs_t downTime; + uint32_t policyFlags; + }; + + struct MotionMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t flags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + bool hovering; + uint32_t policyFlags; + + void setPointers(const MotionEntry* entry); + }; + + std::vector mKeyMementos; + std::vector mMotionMementos; + KeyedVector mFallbackKeys; + + ssize_t findKeyMemento(const KeyEntry* entry) const; + ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + + void addKeyMemento(const KeyEntry* entry, int32_t flags); + void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + + static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); + static bool shouldCancelMotion(const MotionMemento& memento, + const CancelationOptions& options); + }; + + /* Manages the dispatch state associated with a single input channel. */ + class Connection : public RefBase { + protected: + virtual ~Connection(); + + public: + enum Status { + // Everything is peachy. + STATUS_NORMAL, + // An unrecoverable communication error has occurred. + STATUS_BROKEN, + // The input channel has been unregistered. + STATUS_ZOMBIE + }; + + Status status; + sp inputChannel; // never null + bool monitor; + InputPublisher inputPublisher; + InputState inputState; + + // True if the socket is full and no further events can be published until + // the application consumes some of the input. + bool inputPublisherBlocked; + + // Queue of events that need to be published to the connection. + std::deque outboundQueue; + + // Queue of events that have been published to the connection but that have not + // yet received a "finished" response from the application. + std::deque waitQueue; + + explicit Connection(const sp& inputChannel, bool monitor); + + inline const std::string getInputChannelName() const { return inputChannel->getName(); } + + const std::string getWindowName() const; + const char* getStatusLabel() const; + + std::deque::iterator findWaitQueueEntry(uint32_t seq); + }; + + struct Monitor { + sp inputChannel; // never null + + explicit Monitor(const sp& inputChannel); + }; + + enum DropReason { + DROP_REASON_NOT_DROPPED = 0, + DROP_REASON_POLICY = 1, + DROP_REASON_APP_SWITCH = 2, + DROP_REASON_DISABLED = 3, + DROP_REASON_BLOCKED = 4, + DROP_REASON_STALE = 5, + }; + + sp mPolicy; + InputDispatcherConfiguration mConfig; + + std::mutex mLock; + + std::condition_variable mDispatcherIsAlive; + + sp mLooper; + + EventEntry* mPendingEvent GUARDED_BY(mLock); + std::deque mInboundQueue GUARDED_BY(mLock); + std::deque mRecentQueue GUARDED_BY(mLock); + std::deque> mCommandQueue GUARDED_BY(mLock); + + DropReason mLastDropReason GUARDED_BY(mLock); + + void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); + + // Enqueues an inbound event. Returns true if mLooper->wake() should be called. + bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + + // Cleans up input state when dropping an inbound event. + void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); + + // Adds an event to a queue of recent events for debugging purposes. + void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); + + // App switch latency optimization. + bool mAppSwitchSawKeyDown GUARDED_BY(mLock); + nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); + + bool isAppSwitchKeyEvent(KeyEntry* keyEntry); + bool isAppSwitchPendingLocked() REQUIRES(mLock); + void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); + + // Stale event latency optimization. + static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); + + // Blocked event latency optimization. Drops old events when the user intends + // to transfer focus to a new application. + EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); + + sp findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, + bool addOutsideTargets = false, + bool addPortalWindows = false) REQUIRES(mLock); + + // All registered connections mapped by channel file descriptor. + std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); + + struct IBinderHash { + std::size_t operator()(const sp& b) const { + return std::hash{}(b.get()); + } + }; + std::unordered_map, sp, IBinderHash> mInputChannelsByToken + GUARDED_BY(mLock); + + // Finds the display ID of the gesture monitor identified by the provided token. + std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) + REQUIRES(mLock); + + sp getConnectionLocked(const sp& inputChannel) REQUIRES(mLock); + + // Input channels that will receive a copy of all input events sent to the provided display. + std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); + + // Input channels that will receive pointer events that start within the corresponding display. + // These are a bit special when compared to global monitors since they'll cause gesture streams + // to continue even when there isn't a touched window,and have the ability to steal the rest of + // the pointer stream in order to claim it for a system gesture. + std::unordered_map> mGestureMonitorsByDisplay GUARDED_BY(mLock); + + // Event injection and synchronization. + std::condition_variable mInjectionResultAvailable; + bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); + void setInjectionResult(EventEntry* entry, int32_t injectionResult); + + std::condition_variable mInjectionSyncFinished; + void incrementPendingForegroundDispatches(EventEntry* entry); + void decrementPendingForegroundDispatches(EventEntry* entry); + + // Key repeat tracking. + struct KeyRepeatState { + KeyEntry* lastKeyEntry; // or null if no repeat + nsecs_t nextRepeatTime; + } mKeyRepeatState GUARDED_BY(mLock); + + void resetKeyRepeatLocked() REQUIRES(mLock); + KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); + + // Key replacement tracking + struct KeyReplacement { + int32_t keyCode; + int32_t deviceId; + bool operator==(const KeyReplacement& rhs) const { + return keyCode == rhs.keyCode && deviceId == rhs.deviceId; + } + bool operator<(const KeyReplacement& rhs) const { + return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId; + } + }; + // Maps the key code replaced, device id tuple to the key code it was replaced with + KeyedVector mReplacedKeys GUARDED_BY(mLock); + // Process certain Meta + Key combinations + void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode, + int32_t& metaState); + + // Deferred command processing. + bool haveCommandsLocked() const REQUIRES(mLock); + bool runCommandsLockedInterruptible() REQUIRES(mLock); + void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); + + // Input filter processing. + bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); + bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); + + // Inbound event processing. + void drainInboundQueueLocked() REQUIRES(mLock); + void releasePendingEventLocked() REQUIRES(mLock); + void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + + // Dispatch state. + bool mDispatchEnabled GUARDED_BY(mLock); + bool mDispatchFrozen GUARDED_BY(mLock); + bool mInputFilterEnabled GUARDED_BY(mLock); + + std::unordered_map>> mWindowHandlesByDisplay + GUARDED_BY(mLock); + // Get window handles by display, return an empty vector if not found. + std::vector> getWindowHandlesLocked(int32_t displayId) const + REQUIRES(mLock); + sp getWindowHandleLocked(const sp& windowHandleToken) const + REQUIRES(mLock); + sp getInputChannelLocked(const sp& windowToken) const REQUIRES(mLock); + bool hasWindowHandleLocked(const sp& windowHandle) const REQUIRES(mLock); + + /* + * Validate and update InputWindowHandles for a given display. + */ + void updateWindowHandlesForDisplayLocked( + const std::vector>& inputWindowHandles, int32_t displayId) + REQUIRES(mLock); + + // Focus tracking for keys, trackball, etc. + std::unordered_map> mFocusedWindowHandlesByDisplay + GUARDED_BY(mLock); + + // Focus tracking for touch. + struct TouchedWindow { + sp windowHandle; + int32_t targetFlags; + BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set + }; + + // For tracking the offsets we need to apply when adding gesture monitor targets. + struct TouchedMonitor { + Monitor monitor; + float xOffset = 0.f; + float yOffset = 0.f; + + explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); + }; + + struct TouchState { + bool down; + bool split; + int32_t deviceId; // id of the device that is currently down, others are rejected + uint32_t source; // source of the device that is current down, others are rejected + int32_t displayId; // id to the display that currently has a touch, others are rejected + std::vector windows; + + // This collects the portal windows that the touch has gone through. Each portal window + // targets a display (embedded display for most cases). With this info, we can add the + // monitoring channels of the displays touched. + std::vector> portalWindows; + + std::vector gestureMonitors; + + TouchState(); + ~TouchState(); + void reset(); + void copyFrom(const TouchState& other); + void addOrUpdateWindow(const sp& windowHandle, int32_t targetFlags, + BitSet32 pointerIds); + void addPortalWindow(const sp& windowHandle); + void addGestureMonitors(const std::vector& monitors); + void removeWindow(const sp& windowHandle); + void removeWindowByToken(const sp& token); + void filterNonAsIsTouchWindows(); + void filterNonMonitors(); + sp getFirstForegroundWindowHandle() const; + bool isSlippery() const; + }; + + KeyedVector mTouchStatesByDisplay GUARDED_BY(mLock); + TouchState mTempTouchState GUARDED_BY(mLock); + + // Focused applications. + std::unordered_map> mFocusedApplicationHandlesByDisplay + GUARDED_BY(mLock); + + // Top focused display. + int32_t mFocusedDisplayId GUARDED_BY(mLock); + + // Dispatcher state at time of last ANR. + std::string mLastANRState GUARDED_BY(mLock); + + // Dispatch inbound events. + bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) + REQUIRES(mLock); + bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); + bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, + const std::vector& inputTargets) REQUIRES(mLock); + + void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); + void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); + + // Keeping track of ANR timeouts. + enum InputTargetWaitCause { + INPUT_TARGET_WAIT_CAUSE_NONE, + INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, + INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, + }; + + InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); + nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); + nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); + bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); + sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); + + // Contains the last window which received a hover event. + sp mLastHoverWindowHandle GUARDED_BY(mLock); + + // Finding targets for input events. + int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, + const sp& applicationHandle, + const sp& windowHandle, + nsecs_t* nextWakeupTime, const char* reason) + REQUIRES(mLock); + + void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); + + void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, + const sp& inputChannel) + REQUIRES(mLock); + nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); + void resetANRTimeoutsLocked() REQUIRES(mLock); + + int32_t getTargetDisplayId(const EventEntry* entry); + int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, + std::vector& inputTargets, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, + std::vector& inputTargets, + nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions) REQUIRES(mLock); + std::vector findTouchedGestureMonitorsLocked( + int32_t displayId, const std::vector>& portalWindows) + REQUIRES(mLock); + void addGestureMonitors(const std::vector& monitors, + std::vector& outTouchedMonitors, float xOffset = 0, + float yOffset = 0); + + void addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, + BitSet32 pointerIds, std::vector& inputTargets) + REQUIRES(mLock); + void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, + std::vector& inputTargets) REQUIRES(mLock); + void addGlobalMonitoringTargetsLocked(std::vector& inputTargets, int32_t displayId, + float xOffset = 0, float yOffset = 0) REQUIRES(mLock); + + void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); + bool checkInjectionPermission(const sp& windowHandle, + const InjectionState* injectionState); + bool isWindowObscuredAtPointLocked(const sp& windowHandle, int32_t x, + int32_t y) const REQUIRES(mLock); + bool isWindowObscuredLocked(const sp& windowHandle) const REQUIRES(mLock); + std::string getApplicationWindowLabel(const sp& applicationHandle, + const sp& windowHandle); + + std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, + const sp& windowHandle, + const EventEntry* eventEntry, + const char* targetType) REQUIRES(mLock); + + // Manage the dispatch cycle for a single connection. + // These methods are deliberately not Interruptible because doing all of the work + // with the mutex held makes it easier to ensure that connection invariants are maintained. + // If needed, the methods post commands to run later once the critical bits are done. + void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, + EventEntry* eventEntry, const InputTarget* inputTarget) + REQUIRES(mLock); + void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, + EventEntry* eventEntry, const InputTarget* inputTarget) + REQUIRES(mLock); + void enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, + const InputTarget* inputTarget, int32_t dispatchMode) + REQUIRES(mLock); + void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection) + REQUIRES(mLock); + void finishDispatchCycleLocked(nsecs_t currentTime, const sp& connection, + uint32_t seq, bool handled) REQUIRES(mLock); + void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp& connection, + bool notify) REQUIRES(mLock); + void drainDispatchQueue(std::deque& queue); + void releaseDispatchEntry(DispatchEntry* dispatchEntry); + static int handleReceiveCallback(int fd, int events, void* data); + // The action sent should only be of type AMOTION_EVENT_* + void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, + const sp& newToken) REQUIRES(mLock); + + void synthesizeCancelationEventsForAllConnectionsLocked(const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForMonitorsLocked( + const CancelationOptions& options, + std::unordered_map>& monitorsByDisplay) REQUIRES(mLock); + void synthesizeCancelationEventsForInputChannelLocked(const sp& channel, + const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForConnectionLocked(const sp& connection, + const CancelationOptions& options) + REQUIRES(mLock); + + // Splitting motion events across windows. + MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); + + // Reset and drop everything the dispatcher is doing. + void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); + + // Dump state. + void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); + void dumpMonitors(std::string& dump, const std::vector& monitors); + void logDispatchStateLocked() REQUIRES(mLock); + + // Registration. + void removeMonitorChannelLocked(const sp& inputChannel) REQUIRES(mLock); + void removeMonitorChannelLocked( + const sp& inputChannel, + std::unordered_map>& monitorsByDisplay) REQUIRES(mLock); + status_t unregisterInputChannelLocked(const sp& inputChannel, bool notify) + REQUIRES(mLock); + + // Interesting events that we might like to log or tell the framework about. + void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp& connection, + uint32_t seq, bool handled) REQUIRES(mLock); + void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp& connection) + REQUIRES(mLock); + void onFocusChangedLocked(const sp& oldFocus, + const sp& newFocus) REQUIRES(mLock); + void onANRLocked(nsecs_t currentTime, const sp& applicationHandle, + const sp& windowHandle, nsecs_t eventTime, + nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); + + // Outbound policy interactions. + void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + bool afterKeyEventLockedInterruptible(const sp& connection, + DispatchEntry* dispatchEntry, KeyEntry* keyEntry, + bool handled) REQUIRES(mLock); + bool afterMotionEventLockedInterruptible(const sp& connection, + DispatchEntry* dispatchEntry, MotionEntry* motionEntry, + bool handled) REQUIRES(mLock); + void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); + void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + + // Statistics gathering. + void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, + int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); + void traceInboundQueueLengthLocked() REQUIRES(mLock); + void traceOutboundQueueLength(const sp& connection); + void traceWaitQueueLength(const sp& connection); + + sp mReporter; +}; + +/* Enqueues and dispatches input events, endlessly. */ +class InputDispatcherThread : public Thread { +public: + explicit InputDispatcherThread(const sp& dispatcher); + ~InputDispatcherThread(); + +private: + virtual bool threadLoop(); + + sp mDispatcher; +}; + +} // namespace android + +#endif // _UI_INPUT_DISPATCHER_H diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index a86dcbc552..8474b1cb8a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "../InputDispatcher.h" +#include "../dispatcher/InputDispatcher.h" #include -- cgit v1.2.3-59-g8ed1b From e84e6f99d4fdd3fdaf3d8514d44a916175a16e6e Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 29 Aug 2019 17:28:41 -0700 Subject: Divide InputDispatcher into several files. This CL does: 1) Isolate implementation details of InputDispatcher from outside and only expose necessary header files; 2) Move implementation details into android::inputdispatcher namespace; 3) Make input dispatcher a static library for inputflinger to link against; 4) Add InputDispatcherFactory.{h,cpp} to isolate implementation details in InputDispatcher.h from InputManager. Bug: 140139676 Test: Touches on touchscreen can be dispatched to right windows. Change-Id: Ib61c16fd41f3f76f538a3de9b54f31ac304e03a5 --- services/inputflinger/Android.bp | 33 +- services/inputflinger/InputManager.cpp | 4 +- services/inputflinger/InputManager.h | 8 +- services/inputflinger/InputReporter.cpp | 41 - services/inputflinger/dispatcher/Android.bp | 42 + .../inputflinger/dispatcher/CancelationOptions.h | 53 ++ services/inputflinger/dispatcher/Connection.cpp | 64 ++ services/inputflinger/dispatcher/Connection.h | 73 ++ services/inputflinger/dispatcher/Entry.cpp | 258 +++++++ services/inputflinger/dispatcher/Entry.h | 216 ++++++ .../inputflinger/dispatcher/InjectionState.cpp | 42 + services/inputflinger/dispatcher/InjectionState.h | 60 ++ .../inputflinger/dispatcher/InputDispatcher.cpp | 857 +-------------------- services/inputflinger/dispatcher/InputDispatcher.h | 804 +------------------ .../dispatcher/InputDispatcherFactory.cpp | 27 + .../dispatcher/InputDispatcherThread.cpp | 33 + services/inputflinger/dispatcher/InputState.cpp | 373 +++++++++ services/inputflinger/dispatcher/InputState.h | 125 +++ services/inputflinger/dispatcher/InputTarget.cpp | 45 ++ services/inputflinger/dispatcher/InputTarget.h | 117 +++ services/inputflinger/dispatcher/Monitor.cpp | 28 + services/inputflinger/dispatcher/Monitor.h | 41 + services/inputflinger/dispatcher/TouchState.cpp | 159 ++++ services/inputflinger/dispatcher/TouchState.h | 63 ++ services/inputflinger/dispatcher/TouchedWindow.h | 36 + .../include/InputDispatcherConfiguration.h | 44 ++ .../dispatcher/include/InputDispatcherFactory.h | 33 + .../dispatcher/include/InputDispatcherInterface.h | 158 ++++ .../include/InputDispatcherPolicyInterface.h | 123 +++ .../dispatcher/include/InputDispatcherThread.h | 41 + .../inputflinger/include/InputReporterInterface.h | 53 -- services/inputflinger/reporter/Android.bp | 41 + services/inputflinger/reporter/InputReporter.cpp | 41 + .../inputflinger/reporter/InputReporterInterface.h | 53 ++ .../inputflinger/tests/InputDispatcher_test.cpp | 6 +- 35 files changed, 2443 insertions(+), 1752 deletions(-) delete mode 100644 services/inputflinger/InputReporter.cpp create mode 100644 services/inputflinger/dispatcher/Android.bp create mode 100644 services/inputflinger/dispatcher/CancelationOptions.h create mode 100644 services/inputflinger/dispatcher/Connection.cpp create mode 100644 services/inputflinger/dispatcher/Connection.h create mode 100644 services/inputflinger/dispatcher/Entry.cpp create mode 100644 services/inputflinger/dispatcher/Entry.h create mode 100644 services/inputflinger/dispatcher/InjectionState.cpp create mode 100644 services/inputflinger/dispatcher/InjectionState.h create mode 100644 services/inputflinger/dispatcher/InputDispatcherFactory.cpp create mode 100644 services/inputflinger/dispatcher/InputDispatcherThread.cpp create mode 100644 services/inputflinger/dispatcher/InputState.cpp create mode 100644 services/inputflinger/dispatcher/InputState.h create mode 100644 services/inputflinger/dispatcher/InputTarget.cpp create mode 100644 services/inputflinger/dispatcher/InputTarget.h create mode 100644 services/inputflinger/dispatcher/Monitor.cpp create mode 100644 services/inputflinger/dispatcher/Monitor.h create mode 100644 services/inputflinger/dispatcher/TouchState.cpp create mode 100644 services/inputflinger/dispatcher/TouchState.h create mode 100644 services/inputflinger/dispatcher/TouchedWindow.h create mode 100644 services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h create mode 100644 services/inputflinger/dispatcher/include/InputDispatcherFactory.h create mode 100644 services/inputflinger/dispatcher/include/InputDispatcherInterface.h create mode 100644 services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h create mode 100644 services/inputflinger/dispatcher/include/InputDispatcherThread.h delete mode 100644 services/inputflinger/include/InputReporterInterface.h create mode 100644 services/inputflinger/reporter/Android.bp create mode 100644 services/inputflinger/reporter/InputReporter.cpp create mode 100644 services/inputflinger/reporter/InputReporterInterface.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 8649c1ebc6..32dab17420 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -30,7 +30,6 @@ cc_library_shared { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", - "dispatcher/InputDispatcher.cpp", "InputManager.cpp", ], @@ -50,6 +49,10 @@ cc_library_shared { "server_configurable_flags", ], + static_libs: [ + "libinputdispatcher", + ], + cflags: [ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden @@ -58,14 +61,18 @@ cc_library_shared { export_include_dirs: [ ".", "include", - "dispatcher", ], + export_static_lib_headers: [ + "libinputdispatcher", + ], } cc_library_headers { name: "libinputflinger_headers", + header_libs: ["libinputreporter_headers"], export_include_dirs: ["include"], + export_header_lib_headers: ["libinputreporter_headers"], } cc_library_shared { @@ -125,28 +132,6 @@ cc_library_shared { ], } -cc_library_shared { - name: "libinputreporter", - defaults: ["inputflinger_defaults"], - - srcs: [ - "InputReporter.cpp", - ], - - shared_libs: [ - "liblog", - "libutils", - ], - - header_libs: [ - "libinputflinger_headers", - ], - - export_header_lib_headers: [ - "libinputflinger_headers", - ], -} - subdirs = [ "host", "tests", diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 3996cca646..359325faed 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -19,6 +19,8 @@ //#define LOG_NDEBUG 0 #include "InputManager.h" +#include "InputDispatcherFactory.h" +#include "InputDispatcherThread.h" #include "InputReaderFactory.h" #include @@ -33,7 +35,7 @@ namespace android { InputManager::InputManager( const sp& readerPolicy, const sp& dispatcherPolicy) { - mDispatcher = new InputDispatcher(dispatcherPolicy); + mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); mReader = createInputReader(readerPolicy, mClassifier); initialize(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e568df54df..ba0b28786b 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -22,14 +22,15 @@ */ #include "EventHub.h" -#include "InputReaderBase.h" #include "InputClassifier.h" -#include "InputDispatcher.h" #include "InputReader.h" +#include "InputReaderBase.h" +#include +#include +#include #include #include -#include #include #include @@ -39,6 +40,7 @@ namespace android { class InputChannel; +class InputDispatcherThread; /* * The input manager is the core of the system event processing. diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/InputReporter.cpp deleted file mode 100644 index 8d3153c367..0000000000 --- a/services/inputflinger/InputReporter.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InputReporterInterface.h" - -namespace android { - -// --- InputReporter --- - -class InputReporter : public InputReporterInterface { -public: - void reportUnhandledKey(uint32_t sequenceNum) override; - void reportDroppedKey(uint32_t sequenceNum) override; -}; - -void InputReporter::reportUnhandledKey(uint32_t sequenceNum) { - // do nothing -} - -void InputReporter::reportDroppedKey(uint32_t sequenceNum) { - // do nothing -} - -sp createInputReporter() { - return new InputReporter(); -} - -} // namespace android diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp new file mode 100644 index 0000000000..b8c3a808f1 --- /dev/null +++ b/services/inputflinger/dispatcher/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_static { + name: "libinputdispatcher", + defaults: ["inputflinger_defaults"], + srcs: [ + "Connection.cpp", + "Entry.cpp", + "InjectionState.cpp", + "InputDispatcher.cpp", + "InputDispatcherFactory.cpp", + "InputDispatcherThread.cpp", + "InputState.cpp", + "InputTarget.cpp", + "Monitor.cpp", + "TouchState.cpp" + ], + shared_libs: [ + "libbase", + "libcutils", + "libinput", + "libinputreporter", + "libinputflinger_base", + "liblog", + "libui", + "libutils", + ], + + export_include_dirs: ["include"], +} diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h new file mode 100644 index 0000000000..99e2108dbf --- /dev/null +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H +#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H + +#include + +namespace android::inputdispatcher { + +/* Specifies which events are to be canceled and why. */ +struct CancelationOptions { + enum Mode { + CANCEL_ALL_EVENTS = 0, + CANCEL_POINTER_EVENTS = 1, + CANCEL_NON_POINTER_EVENTS = 2, + CANCEL_FALLBACK_EVENTS = 3, + }; + + // The criterion to use to determine which events should be canceled. + Mode mode; + + // Descriptive reason for the cancelation. + const char* reason; + + // The specific keycode of the key event to cancel, or nullopt to cancel any key event. + std::optional keyCode = std::nullopt; + + // The specific device id of events to cancel, or nullopt to cancel events from any device. + std::optional deviceId = std::nullopt; + + // The specific display id of events to cancel, or nullopt to cancel events on any display. + std::optional displayId = std::nullopt; + + CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp new file mode 100644 index 0000000000..6f82f4fd10 --- /dev/null +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Connection.h" + +#include "Entry.h" + +namespace android::inputdispatcher { + +Connection::Connection(const sp& inputChannel, bool monitor) + : status(STATUS_NORMAL), + inputChannel(inputChannel), + monitor(monitor), + inputPublisher(inputChannel), + inputPublisherBlocked(false) {} + +Connection::~Connection() {} + +const std::string Connection::getWindowName() const { + if (inputChannel != nullptr) { + return inputChannel->getName(); + } + if (monitor) { + return "monitor"; + } + return "?"; +} + +const char* Connection::getStatusLabel() const { + switch (status) { + case STATUS_NORMAL: + return "NORMAL"; + case STATUS_BROKEN: + return "BROKEN"; + case STATUS_ZOMBIE: + return "ZOMBIE"; + default: + return "UNKNOWN"; + } +} + +std::deque::iterator Connection::findWaitQueueEntry(uint32_t seq) { + for (std::deque::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { + if ((*it)->seq == seq) { + return it; + } + } + return waitQueue.end(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h new file mode 100644 index 0000000000..8423010502 --- /dev/null +++ b/services/inputflinger/dispatcher/Connection.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H +#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H + +#include "InputState.h" + +#include +#include + +namespace android::inputdispatcher { + +struct DispatchEntry; + +/* Manages the dispatch state associated with a single input channel. */ +class Connection : public RefBase { +protected: + virtual ~Connection(); + +public: + enum Status { + // Everything is peachy. + STATUS_NORMAL, + // An unrecoverable communication error has occurred. + STATUS_BROKEN, + // The input channel has been unregistered. + STATUS_ZOMBIE + }; + + Status status; + sp inputChannel; // never null + bool monitor; + InputPublisher inputPublisher; + InputState inputState; + + // True if the socket is full and no further events can be published until + // the application consumes some of the input. + bool inputPublisherBlocked; + + // Queue of events that need to be published to the connection. + std::deque outboundQueue; + + // Queue of events that have been published to the connection but that have not + // yet received a "finished" response from the application. + std::deque waitQueue; + + explicit Connection(const sp& inputChannel, bool monitor); + + inline const std::string getInputChannelName() const { return inputChannel->getName(); } + + const std::string getWindowName() const; + const char* getStatusLabel() const; + + std::deque::iterator findWaitQueueEntry(uint32_t seq); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp new file mode 100644 index 0000000000..640a69a2ec --- /dev/null +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Entry.h" + +#include "Connection.h" + +#include +#include +#include + +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +static std::string motionActionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return StringPrintf("%" PRId32, action); +} + +static std::string keyActionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return StringPrintf("%" PRId32, action); +} + +// --- EventEntry --- + +EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags) + : sequenceNum(sequenceNum), + refCount(1), + type(type), + eventTime(eventTime), + policyFlags(policyFlags), + injectionState(nullptr), + dispatchInProgress(false) {} + +EventEntry::~EventEntry() { + releaseInjectionState(); +} + +void EventEntry::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +void EventEntry::releaseInjectionState() { + if (injectionState) { + injectionState->release(); + injectionState = nullptr; + } +} + +// --- ConfigurationChangedEntry --- + +ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime) + : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {} + +ConfigurationChangedEntry::~ConfigurationChangedEntry() {} + +void ConfigurationChangedEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); +} + +// --- DeviceResetEntry --- + +DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) + : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} + +DeviceResetEntry::~DeviceResetEntry() {} + +void DeviceResetEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); +} + +// --- KeyEntry --- + +KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime) + : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + flags(flags), + keyCode(keyCode), + scanCode(scanCode), + metaState(metaState), + repeatCount(repeatCount), + downTime(downTime), + syntheticRepeat(false), + interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), + interceptKeyWakeupTime(0) {} + +KeyEntry::~KeyEntry() {} + +void KeyEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " + "repeatCount=%d), policyFlags=0x%08x", + deviceId, source, displayId, keyActionToString(action).c_str(), flags, + keyCode, scanCode, metaState, repeatCount, policyFlags); +} + +void KeyEntry::recycle() { + releaseInjectionState(); + + dispatchInProgress = false; + syntheticRepeat = false; + interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + interceptKeyWakeupTime = 0; +} + +// --- MotionEntry --- + +MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xOffset, float yOffset) + : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + eventTime(eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), + pointerCount(pointerCount) { + for (uint32_t i = 0; i < pointerCount; i++) { + this->pointerProperties[i].copyFrom(pointerProperties[i]); + this->pointerCoords[i].copyFrom(pointerCoords[i]); + if (xOffset || yOffset) { + this->pointerCoords[i].applyOffset(xOffset, yOffset); + } + } +} + +MotionEntry::~MotionEntry() {} + +void MotionEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " + "buttonState=0x%08x, " + "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " + "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), + actionButton, flags, metaState, buttonState, + motionClassificationToString(classification), edgeFlags, xPrecision, + yPrecision, xCursorPosition, yCursorPosition); + + for (uint32_t i = 0; i < pointerCount; i++) { + if (i) { + msg += ", "; + } + msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(), + pointerCoords[i].getY()); + } + msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); +} + +// --- DispatchEntry --- + +volatile int32_t DispatchEntry::sNextSeqAtomic; + +DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, + float yOffset, float globalScaleFactor, float windowXScale, + float windowYScale) + : seq(nextSeq()), + eventEntry(eventEntry), + targetFlags(targetFlags), + xOffset(xOffset), + yOffset(yOffset), + globalScaleFactor(globalScaleFactor), + windowXScale(windowXScale), + windowYScale(windowYScale), + deliveryTime(0), + resolvedAction(0), + resolvedFlags(0) { + eventEntry->refCount += 1; +} + +DispatchEntry::~DispatchEntry() { + eventEntry->release(); +} + +uint32_t DispatchEntry::nextSeq() { + // Sequence number 0 is reserved and will never be returned. + uint32_t seq; + do { + seq = android_atomic_inc(&sNextSeqAtomic); + } while (!seq); + return seq; +} + +// --- CommandEntry --- + +CommandEntry::CommandEntry(Command command) + : command(command), + eventTime(0), + keyEntry(nullptr), + userActivityEventType(0), + seq(0), + handled(false) {} + +CommandEntry::~CommandEntry() {} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h new file mode 100644 index 0000000000..a9e22f19f1 --- /dev/null +++ b/services/inputflinger/dispatcher/Entry.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H +#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H + +#include "InjectionState.h" +#include "InputTarget.h" + +#include +#include +#include +#include +#include +#include + +namespace android::inputdispatcher { + +struct EventEntry { + enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; + + uint32_t sequenceNum; + mutable int32_t refCount; + int32_t type; + nsecs_t eventTime; + uint32_t policyFlags; + InjectionState* injectionState; + + bool dispatchInProgress; // initially false, set to true while dispatching + + inline bool isInjected() const { return injectionState != nullptr; } + + void release(); + + virtual void appendDescription(std::string& msg) const = 0; + +protected: + EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); + virtual ~EventEntry(); + void releaseInjectionState(); +}; + +struct ConfigurationChangedEntry : EventEntry { + explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~ConfigurationChangedEntry(); +}; + +struct DeviceResetEntry : EventEntry { + int32_t deviceId; + + DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~DeviceResetEntry(); +}; + +struct KeyEntry : EventEntry { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + + bool syntheticRepeat; // set to true for synthetic key repeats + + enum InterceptKeyResult { + INTERCEPT_KEY_RESULT_UNKNOWN, + INTERCEPT_KEY_RESULT_SKIP, + INTERCEPT_KEY_RESULT_CONTINUE, + INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, + }; + InterceptKeyResult interceptKeyResult; // set based on the interception result + nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER + + KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime); + virtual void appendDescription(std::string& msg) const; + void recycle(); + +protected: + virtual ~KeyEntry(); +}; + +struct MotionEntry : EventEntry { + nsecs_t eventTime; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + + MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xOffset, float yOffset); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~MotionEntry(); +}; + +// Tracks the progress of dispatching a particular event to a particular connection. +struct DispatchEntry { + const uint32_t seq; // unique sequence number, never 0 + + EventEntry* eventEntry; // the event to dispatch + int32_t targetFlags; + float xOffset; + float yOffset; + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + nsecs_t deliveryTime; // time when the event was actually delivered + + // Set to the resolved action and flags when the event is enqueued. + int32_t resolvedAction; + int32_t resolvedFlags; + + DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, + float globalScaleFactor, float windowXScale, float windowYScale); + ~DispatchEntry(); + + inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } + + inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + +private: + static volatile int32_t sNextSeqAtomic; + + static uint32_t nextSeq(); +}; + +class InputDispatcher; +// A command entry captures state and behavior for an action to be performed in the +// dispatch loop after the initial processing has taken place. It is essentially +// a kind of continuation used to postpone sensitive policy interactions to a point +// in the dispatch loop where it is safe to release the lock (generally after finishing +// the critical parts of the dispatch cycle). +// +// The special thing about commands is that they can voluntarily release and reacquire +// the dispatcher lock at will. Initially when the command starts running, the +// dispatcher lock is held. However, if the command needs to call into the policy to +// do some work, it can release the lock, do the work, then reacquire the lock again +// before returning. +// +// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch +// never calls into the policy while holding its lock. +// +// Commands are implicitly 'LockedInterruptible'. +struct CommandEntry; +typedef std::function Command; + +class Connection; +struct CommandEntry { + explicit CommandEntry(Command command); + ~CommandEntry(); + + Command command; + + // parameters for the command (usage varies by command) + sp connection; + nsecs_t eventTime; + KeyEntry* keyEntry; + sp inputApplicationHandle; + std::string reason; + int32_t userActivityEventType; + uint32_t seq; + bool handled; + sp inputChannel; + sp oldToken; + sp newToken; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp new file mode 100644 index 0000000000..b2d0a26a37 --- /dev/null +++ b/services/inputflinger/dispatcher/InjectionState.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InjectionState.h" + +#include + +namespace android::inputdispatcher { + +InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) + : refCount(1), + injectorPid(injectorPid), + injectorUid(injectorUid), + injectionResult(INPUT_EVENT_INJECTION_PENDING), + injectionIsAsync(false), + pendingForegroundDispatches(0) {} + +InjectionState::~InjectionState() {} + +void InjectionState::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h new file mode 100644 index 0000000000..311a0f11ef --- /dev/null +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H + +#include "InputDispatcherInterface.h" + +#include + +namespace android::inputdispatcher { + +/* + * Constants used to determine the input event injection synchronization mode. + */ +enum { + /* Injection is asynchronous and is assumed always to be successful. */ + INPUT_EVENT_INJECTION_SYNC_NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, +}; + +struct InjectionState { + mutable int32_t refCount; + + int32_t injectorPid; + int32_t injectorUid; + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress + + InjectionState(int32_t injectorPid, int32_t injectorUid); + void release(); + +private: + ~InjectionState(); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4b8c51be5e..039462e23b 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -45,6 +45,8 @@ #include "InputDispatcher.h" +#include "Connection.h" + #include #include #include @@ -68,7 +70,7 @@ using android::base::StringPrintf; -namespace android { +namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. @@ -98,9 +100,6 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -109,54 +108,6 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } -static std::string motionActionToString(int32_t action) { - // Convert MotionEvent action to string - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; - case AMOTION_EVENT_ACTION_UP: - return "UP"; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; - case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string keyActionToString(int32_t action) { - // Convert KeyEvent action to string - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - return "DOWN"; - case AKEY_EVENT_ACTION_UP: - return "UP"; - case AKEY_EVENT_ACTION_MULTIPLE: - return "MULTIPLE"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string dispatchModeToString(int32_t dispatchMode) { - switch (dispatchMode) { - case InputTarget::FLAG_DISPATCH_AS_IS: - return "DISPATCH_AS_IS"; - case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: - return "DISPATCH_AS_OUTSIDE"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: - return "DISPATCH_AS_HOVER_ENTER"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: - return "DISPATCH_AS_HOVER_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: - return "DISPATCH_AS_SLIPPERY_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: - return "DISPATCH_AS_SLIPPERY_ENTER"; - } - return StringPrintf("%" PRId32, dispatchMode); -} - static inline int32_t getMotionEventActionPointerIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; @@ -612,7 +563,7 @@ sp InputDispatcher::findTouchedWindowAtLocked(int32_t display return nullptr; } -std::vector InputDispatcher::findTouchedGestureMonitorsLocked( +std::vector InputDispatcher::findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector>& portalWindows) { std::vector touchedMonitors; @@ -787,7 +738,7 @@ void InputDispatcher::resetKeyRepeatLocked() { } } -InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { +KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { KeyEntry* entry = mKeyRepeatState.lastKeyEntry; // Reuse the repeated key entry if it is otherwise unreferenced. @@ -2589,8 +2540,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } } -InputDispatcher::MotionEntry* InputDispatcher::splitMotionEvent( - const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { +MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, + BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); uint32_t splitPointerIndexMap[MAX_POINTERS]; @@ -4101,8 +4052,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked( - const sp& inputChannel) { +sp InputDispatcher::getConnectionLocked(const sp& inputChannel) { if (inputChannel == nullptr) { return nullptr; } @@ -4275,8 +4225,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c const bool handled = commandEntry->handled; // Handle post-event policy actions. - std::deque::iterator dispatchEntryIt = - connection->findWaitQueueEntry(seq); + std::deque::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq); if (dispatchEntryIt == connection->waitQueue.end()) { return; } @@ -4570,790 +4519,4 @@ void InputDispatcher::monitor() { mDispatcherIsAlive.wait(_l); } -// --- InputDispatcher::InjectionState --- - -InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) - : refCount(1), - injectorPid(injectorPid), - injectorUid(injectorUid), - injectionResult(INPUT_EVENT_INJECTION_PENDING), - injectionIsAsync(false), - pendingForegroundDispatches(0) {} - -InputDispatcher::InjectionState::~InjectionState() {} - -void InputDispatcher::InjectionState::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - -// --- InputDispatcher::EventEntry --- - -InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, - uint32_t policyFlags) - : sequenceNum(sequenceNum), - refCount(1), - type(type), - eventTime(eventTime), - policyFlags(policyFlags), - injectionState(nullptr), - dispatchInProgress(false) {} - -InputDispatcher::EventEntry::~EventEntry() { - releaseInjectionState(); -} - -void InputDispatcher::EventEntry::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - -void InputDispatcher::EventEntry::releaseInjectionState() { - if (injectionState) { - injectionState->release(); - injectionState = nullptr; - } -} - -// --- InputDispatcher::ConfigurationChangedEntry --- - -InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, - nsecs_t eventTime) - : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {} - -InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {} - -void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); -} - -// --- InputDispatcher::DeviceResetEntry --- - -InputDispatcher::DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId) - : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} - -InputDispatcher::DeviceResetEntry::~DeviceResetEntry() {} - -void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); -} - -// --- InputDispatcher::KeyEntry --- - -InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime) - : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), - deviceId(deviceId), - source(source), - displayId(displayId), - action(action), - flags(flags), - keyCode(keyCode), - scanCode(scanCode), - metaState(metaState), - repeatCount(repeatCount), - downTime(downTime), - syntheticRepeat(false), - interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), - interceptKeyWakeupTime(0) {} - -InputDispatcher::KeyEntry::~KeyEntry() {} - -void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " - "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " - "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, keyActionToString(action).c_str(), flags, - keyCode, scanCode, metaState, repeatCount, policyFlags); -} - -void InputDispatcher::KeyEntry::recycle() { - releaseInjectionState(); - - dispatchInProgress = false; - syntheticRepeat = false; - interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; - interceptKeyWakeupTime = 0; -} - -// --- InputDispatcher::MotionEntry --- - -InputDispatcher::MotionEntry::MotionEntry( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset) - : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), - eventTime(eventTime), - deviceId(deviceId), - source(source), - displayId(displayId), - action(action), - actionButton(actionButton), - flags(flags), - metaState(metaState), - buttonState(buttonState), - classification(classification), - edgeFlags(edgeFlags), - xPrecision(xPrecision), - yPrecision(yPrecision), - xCursorPosition(xCursorPosition), - yCursorPosition(yCursorPosition), - downTime(downTime), - pointerCount(pointerCount) { - for (uint32_t i = 0; i < pointerCount; i++) { - this->pointerProperties[i].copyFrom(pointerProperties[i]); - this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } - } -} - -InputDispatcher::MotionEntry::~MotionEntry() {} - -void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 - ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " - "buttonState=0x%08x, " - "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " - "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), - actionButton, flags, metaState, buttonState, - motionClassificationToString(classification), edgeFlags, xPrecision, - yPrecision, xCursorPosition, yCursorPosition); - - for (uint32_t i = 0; i < pointerCount; i++) { - if (i) { - msg += ", "; - } - msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(), - pointerCoords[i].getY()); - } - msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); -} - -// --- InputDispatcher::DispatchEntry --- - -volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; - -InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, - float xOffset, float yOffset, float globalScaleFactor, - float windowXScale, float windowYScale) - : seq(nextSeq()), - eventEntry(eventEntry), - targetFlags(targetFlags), - xOffset(xOffset), - yOffset(yOffset), - globalScaleFactor(globalScaleFactor), - windowXScale(windowXScale), - windowYScale(windowYScale), - deliveryTime(0), - resolvedAction(0), - resolvedFlags(0) { - eventEntry->refCount += 1; -} - -InputDispatcher::DispatchEntry::~DispatchEntry() { - eventEntry->release(); -} - -uint32_t InputDispatcher::DispatchEntry::nextSeq() { - // Sequence number 0 is reserved and will never be returned. - uint32_t seq; - do { - seq = android_atomic_inc(&sNextSeqAtomic); - } while (!seq); - return seq; -} - -// --- InputDispatcher::InputState --- - -InputDispatcher::InputState::InputState() {} - -InputDispatcher::InputState::~InputState() {} - -bool InputDispatcher::InputState::isNeutral() const { - return mKeyMementos.empty() && mMotionMementos.empty(); -} - -bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source, - int32_t displayId) const { - for (const MotionMemento& memento : mMotionMementos) { - if (memento.deviceId == deviceId && memento.source == source && - memento.displayId == displayId && memento.hovering) { - return true; - } - } - return false; -} - -bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) { - switch (action) { - case AKEY_EVENT_ACTION_UP: { - if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { - for (size_t i = 0; i < mFallbackKeys.size();) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { - mFallbackKeys.removeItemsAt(i); - } else { - i += 1; - } - } - } - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - return true; - } - /* FIXME: We can't just drop the key up event because that prevents creating - * popup windows that are automatically shown when a key is held and then - * dismissed when the key is released. The problem is that the popup will - * not have received the original key down, so the key up will be considered - * to be inconsistent with its observed state. We could perhaps handle this - * by synthesizing a key down but that will cause other problems. - * - * So for now, allow inconsistent key up events to be dispatched. - * - #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " - "keyCode=%d, scanCode=%d", - entry->deviceId, entry->source, entry->keyCode, entry->scanCode); - #endif - return false; - */ - return true; - } - - case AKEY_EVENT_ACTION_DOWN: { - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - } - addKeyMemento(entry, flags); - return true; - } - - default: - return true; - } -} - -bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action, - int32_t flags) { - int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; - switch (actionMasked) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " - "displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, false /*hovering*/); - return true; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_MOVE: { - if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { - // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need - // to generate cancellation events for these since they're based in relative rather - // than absolute units. - return true; - } - - ssize_t index = findMotionMemento(entry, false /*hovering*/); - - if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { - // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all - // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. - // Any other value and we need to track the motion so we can send cancellation - // events for anything generating fallback events (e.g. DPad keys for joystick - // movements). - if (index >= 0) { - if (entry->pointerCoords[0].isEmpty()) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } else { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - } - } else if (!entry->pointerCoords[0].isEmpty()) { - addMotionMemento(entry, flags, false /*hovering*/); - } - - // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. - return true; - } - if (index >= 0) { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion pointer up/down or move event: " - "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_EXIT: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " - "displayId=%" PRId32, - entry->deviceId, entry->source, entry->displayId); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, true /*hovering*/); - return true; - } - - default: - return true; - } -} - -ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { - for (size_t i = 0; i < mKeyMementos.size(); i++) { - const KeyMemento& memento = mKeyMementos[i]; - if (memento.deviceId == entry->deviceId && memento.source == entry->source && - memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && - memento.scanCode == entry->scanCode) { - return i; - } - } - return -1; -} - -ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, - bool hovering) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.deviceId == entry->deviceId && memento.source == entry->source && - memento.displayId == entry->displayId && memento.hovering == hovering) { - return i; - } - } - return -1; -} - -void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { - KeyMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.metaState = entry->metaState; - memento.flags = flags; - memento.downTime = entry->downTime; - memento.policyFlags = entry->policyFlags; - mKeyMementos.push_back(memento); -} - -void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, - bool hovering) { - MotionMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.flags = flags; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.xCursorPosition = entry->xCursorPosition; - memento.yCursorPosition = entry->yCursorPosition; - memento.downTime = entry->downTime; - memento.setPointers(entry); - memento.hovering = hovering; - memento.policyFlags = entry->policyFlags; - mMotionMementos.push_back(memento); -} - -void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { - pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < entry->pointerCount; i++) { - pointerProperties[i].copyFrom(entry->pointerProperties[i]); - pointerCoords[i].copyFrom(entry->pointerCoords[i]); - } -} - -void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, - std::vector& outEvents, - const CancelationOptions& options) { - for (KeyMemento& memento : mKeyMementos) { - if (shouldCancelKey(memento, options)) { - outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, - memento.policyFlags, AKEY_EVENT_ACTION_UP, - memento.flags | AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, memento.metaState, - 0, memento.downTime)); - } - } - - for (const MotionMemento& memento : mMotionMementos) { - if (shouldCancelMotion(memento, options)) { - const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT - : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back( - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, action, - 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, memento.pointerCount, - memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); - } - } -} - -void InputDispatcher::InputState::clear() { - mKeyMementos.clear(); - mMotionMementos.clear(); - mFallbackKeys.clear(); -} - -void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { - for (size_t j = 0; j < other.mMotionMementos.size();) { - const MotionMemento& otherMemento = other.mMotionMementos[j]; - if (memento.deviceId == otherMemento.deviceId && - memento.source == otherMemento.source && - memento.displayId == otherMemento.displayId) { - other.mMotionMementos.erase(other.mMotionMementos.begin() + j); - } else { - j += 1; - } - } - other.mMotionMementos.push_back(memento); - } - } -} - -int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - return index >= 0 ? mFallbackKeys.valueAt(index) : -1; -} - -void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - if (index >= 0) { - mFallbackKeys.replaceValueAt(index, fallbackKeyCode); - } else { - mFallbackKeys.add(originalKeyCode, fallbackKeyCode); - } -} - -void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) { - mFallbackKeys.removeItem(originalKeyCode); -} - -bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, - const CancelationOptions& options) { - if (options.keyCode && memento.keyCode != options.keyCode.value()) { - return false; - } - - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return true; - case CancelationOptions::CANCEL_FALLBACK_EVENTS: - return memento.flags & AKEY_EVENT_FLAG_FALLBACK; - default: - return false; - } -} - -bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options) { - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - return true; - case CancelationOptions::CANCEL_POINTER_EVENTS: - return memento.source & AINPUT_SOURCE_CLASS_POINTER; - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); - default: - return false; - } -} - -// --- InputDispatcher::Connection --- - -InputDispatcher::Connection::Connection(const sp& inputChannel, bool monitor) - : status(STATUS_NORMAL), - inputChannel(inputChannel), - monitor(monitor), - inputPublisher(inputChannel), - inputPublisherBlocked(false) {} - -InputDispatcher::Connection::~Connection() {} - -const std::string InputDispatcher::Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} - -const char* InputDispatcher::Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - - case STATUS_BROKEN: - return "BROKEN"; - - case STATUS_ZOMBIE: - return "ZOMBIE"; - - default: - return "UNKNOWN"; - } -} - -std::deque::iterator -InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (std::deque::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { - if ((*it)->seq == seq) { - return it; - } - } - return waitQueue.end(); -} - -// --- InputDispatcher::Monitor -InputDispatcher::Monitor::Monitor(const sp& inputChannel) - : inputChannel(inputChannel) {} - -// --- InputDispatcher::CommandEntry --- -// -InputDispatcher::CommandEntry::CommandEntry(Command command) - : command(command), - eventTime(0), - keyEntry(nullptr), - userActivityEventType(0), - seq(0), - handled(false) {} - -InputDispatcher::CommandEntry::~CommandEntry() {} - -// --- InputDispatcher::TouchedMonitor --- -InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, - float yOffset) - : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {} - -// --- InputDispatcher::TouchState --- - -InputDispatcher::TouchState::TouchState() - : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {} - -InputDispatcher::TouchState::~TouchState() {} - -void InputDispatcher::TouchState::reset() { - down = false; - split = false; - deviceId = -1; - source = 0; - displayId = ADISPLAY_ID_NONE; - windows.clear(); - portalWindows.clear(); - gestureMonitors.clear(); -} - -void InputDispatcher::TouchState::copyFrom(const TouchState& other) { - down = other.down; - split = other.split; - deviceId = other.deviceId; - source = other.source; - displayId = other.displayId; - windows = other.windows; - portalWindows = other.portalWindows; - gestureMonitors = other.gestureMonitors; -} - -void InputDispatcher::TouchState::addOrUpdateWindow(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows[i]; - if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.targetFlags |= targetFlags; - if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; - } - touchedWindow.pointerIds.value |= pointerIds.value; - return; - } - } - - TouchedWindow touchedWindow; - touchedWindow.windowHandle = windowHandle; - touchedWindow.targetFlags = targetFlags; - touchedWindow.pointerIds = pointerIds; - windows.push_back(touchedWindow); -} - -void InputDispatcher::TouchState::addPortalWindow(const sp& windowHandle) { - size_t numWindows = portalWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - if (portalWindows[i] == windowHandle) { - return; - } - } - portalWindows.push_back(windowHandle); -} - -void InputDispatcher::TouchState::addGestureMonitors( - const std::vector& newMonitors) { - const size_t newSize = gestureMonitors.size() + newMonitors.size(); - gestureMonitors.reserve(newSize); - gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors), - std::end(newMonitors)); -} - -void InputDispatcher::TouchState::removeWindow(const sp& windowHandle) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle == windowHandle) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::removeWindowByToken(const sp& token) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle->getToken() == token) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { - for (size_t i = 0; i < windows.size();) { - TouchedWindow& window = windows[i]; - if (window.targetFlags & - (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { - window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; - window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; - i += 1; - } else { - windows.erase(windows.begin() + i); - } - } -} - -void InputDispatcher::TouchState::filterNonMonitors() { - windows.clear(); - portalWindows.clear(); -} - -sp InputDispatcher::TouchState::getFirstForegroundWindowHandle() const { - for (size_t i = 0; i < windows.size(); i++) { - const TouchedWindow& window = windows[i]; - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - return window.windowHandle; - } - } - return nullptr; -} - -bool InputDispatcher::TouchState::isSlippery() const { - // Must have exactly one foreground window. - bool haveSlipperyForegroundWindow = false; - for (const TouchedWindow& window : windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - if (haveSlipperyForegroundWindow || - !(window.windowHandle->getInfo()->layoutParamsFlags & - InputWindowInfo::FLAG_SLIPPERY)) { - return false; - } - haveSlipperyForegroundWindow = true; - } - } - return haveSlipperyForegroundWindow; -} - -// --- InputDispatcherThread --- - -InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) - : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {} - -InputDispatcherThread::~InputDispatcherThread() {} - -bool InputDispatcherThread::threadLoop() { - mDispatcher->dispatchOnce(); - return true; -} - -} // namespace android +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index a90f958e79..f2c04028e4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -17,371 +17,42 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H -#include -#include +#include "CancelationOptions.h" +#include "Entry.h" +#include "InjectionState.h" +#include "InputDispatcherConfiguration.h" +#include "InputDispatcherInterface.h" +#include "InputDispatcherPolicyInterface.h" +#include "InputState.h" +#include "InputTarget.h" +#include "Monitor.h" +#include "TouchState.h" +#include "TouchedWindow.h" + #include #include #include #include +#include +#include #include +#include #include #include #include #include #include #include -#include -#include - -#include -#include -#include #include +#include #include -#include "InputListener.h" -#include "InputReporterInterface.h" - -namespace android { +#include +#include -/* - * Constants used to report the outcome of input event injection. - */ -enum { - /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ - INPUT_EVENT_INJECTION_PENDING = -1, - - /* Injection succeeded. */ - INPUT_EVENT_INJECTION_SUCCEEDED = 0, - - /* Injection failed because the injector did not have permission to inject - * into the application with input focus. */ - INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, - - /* Injection failed because there were no available input targets. */ - INPUT_EVENT_INJECTION_FAILED = 2, - - /* Injection failed due to a timeout. */ - INPUT_EVENT_INJECTION_TIMED_OUT = 3 -}; - -/* - * Constants used to determine the input event injection synchronization mode. - */ -enum { - /* Injection is asynchronous and is assumed always to be successful. */ - INPUT_EVENT_INJECTION_SYNC_NONE = 0, - - /* Waits for previous events to be dispatched so that the input dispatcher can determine - * whether input event injection willbe permitted based on the current input focus. - * Does not wait for the input event to finish processing. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, +namespace android::inputdispatcher { - /* Waits for the input event to be completely processed. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, -}; - -/* - * An input target specifies how an input event is to be dispatched to a particular window - * including the window's input channel, control flags, a timeout, and an X / Y offset to - * be added to input event coordinates to compensate for the absolute position of the - * window area. - */ -struct InputTarget { - enum { - /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - FLAG_ZERO_COORDS = 1 << 3, - - /* This flag indicates that the event should be sent as is. - * Should always be set unless the event is to be transmuted. */ - FLAG_DISPATCH_AS_IS = 1 << 8, - - /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside - * of the area of this target and so should instead be delivered as an - * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, - - /* This flag indicates that a hover sequence is starting in the given window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, - - /* This flag indicates that a hover event happened outside of a window which handled - * previous hover events, signifying the end of the current hover sequence for that - * window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, - - /* This flag indicates that the event should be canceled. - * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips - * outside of a window. */ - FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, - - /* This flag indicates that the event should be dispatched as an initial down. - * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips - * into a new window. */ - FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, - - /* Mask for all dispatch modes. */ - FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | - FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | - FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - - }; - - // The input channel to be targeted. - sp inputChannel; - - // Flags for the input target. - int32_t flags; - - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset, yOffset; - - // Scaling factor to apply to MotionEvent as it is delivered. - // (ignored for KeyEvents) - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - - // The subset of pointer ids to include in motion events dispatched to this input target - // if FLAG_SPLIT is set. - BitSet32 pointerIds; -}; - -/* - * Input dispatcher configuration. - * - * Specifies various options that modify the behavior of the input dispatcher. - * The values provided here are merely defaults. The actual values will come from ViewConfiguration - * and are passed into the dispatcher during initialization. - */ -struct InputDispatcherConfiguration { - // The key repeat initial timeout. - nsecs_t keyRepeatTimeout; - - // The key repeat inter-key delay. - nsecs_t keyRepeatDelay; - - InputDispatcherConfiguration() - : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} -}; - -/* - * Input dispatcher policy interface. - * - * The input reader policy is used by the input reader to interact with the Window Manager - * and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class InputDispatcherPolicyInterface : public virtual RefBase { -protected: - InputDispatcherPolicyInterface() {} - virtual ~InputDispatcherPolicyInterface() {} - -public: - /* Notifies the system that a configuration change has occurred. */ - virtual void notifyConfigurationChanged(nsecs_t when) = 0; - - /* Notifies the system that an application is not responding. - * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyANR(const sp& inputApplicationHandle, - const sp& token, const std::string& reason) = 0; - - /* Notifies the system that an input channel is unrecoverably broken. */ - virtual void notifyInputChannelBroken(const sp& token) = 0; - virtual void notifyFocusChanged(const sp& oldToken, const sp& newToken) = 0; - - /* Gets the input dispatcher configuration. */ - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; - - /* Filters an input event. - * Return true to dispatch the event unmodified, false to consume the event. - * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED - * to injectInputEvent. - */ - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; - - /* Intercepts a key event immediately before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; - - /* Intercepts a touch, trackball or other motion event before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, - uint32_t& policyFlags) = 0; - - /* Allows the policy a chance to intercept a key before dispatching. */ - virtual nsecs_t interceptKeyBeforeDispatching(const sp& token, - const KeyEvent* keyEvent, - uint32_t policyFlags) = 0; - - /* Allows the policy a chance to perform default processing for an unhandled key. - * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ - virtual bool dispatchUnhandledKey(const sp& token, const KeyEvent* keyEvent, - uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; - - /* Notifies the policy about switch events. - */ - virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, - uint32_t policyFlags) = 0; - - /* Poke user activity for an event dispatched to a window. */ - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; - - /* Checks whether a given application pid/uid has permission to inject input events - * into other applications. - * - * This method is special in that its implementation promises to be non-reentrant and - * is safe to call while holding other locks. (Most other methods make no such guarantees!) - */ - virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, - int32_t injectorUid) = 0; - - /* Notifies the policy that a pointer down event has occurred outside the current focused - * window. - * - * The touchedToken passed as an argument is the window that received the input event. - */ - virtual void onPointerDownOutsideFocus(const sp& touchedToken) = 0; -}; - -/* Notifies the system about input events generated by the input reader. - * The dispatcher is expected to be mostly asynchronous. */ -class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { -protected: - InputDispatcherInterface() {} - virtual ~InputDispatcherInterface() {} - -public: - /* Dumps the state of the input dispatcher. - * - * This method may be called on any thread (usually by the input manager). */ - virtual void dump(std::string& dump) = 0; - - /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ - virtual void monitor() = 0; - - /* Runs a single iteration of the dispatch loop. - * Nominally processes one queued event, a timeout, or a response from an input consumer. - * - * This method should only be called on the input dispatcher thread. - */ - virtual void dispatchOnce() = 0; - - /* Injects an input event and optionally waits for sync. - * The synchronization mode determines whether the method blocks while waiting for - * input injection to proceed. - * Returns one of the INPUT_EVENT_INJECTION_XXX constants. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) = 0; - - /* Sets the list of input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputWindows( - const std::vector>& inputWindowHandles, int32_t displayId, - const sp& setInputWindowsListener = nullptr) = 0; - - /* Sets the focused application on the given display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedApplication( - int32_t displayId, const sp& inputApplicationHandle) = 0; - - /* Sets the focused display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedDisplay(int32_t displayId) = 0; - - /* Sets the input dispatching mode. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; - - /* Sets whether input event filtering is enabled. - * When enabled, incoming input events are sent to the policy's filterInputEvent - * method instead of being dispatched. The filter is expected to use - * injectInputEvent to inject the events it would like to have dispatched. - * It should include POLICY_FLAG_FILTERED in the policy flags during injection. - */ - virtual void setInputFilterEnabled(bool enabled) = 0; - - /* Transfers touch focus from one window to another window. - * - * Returns true on success. False if the window did not actually have touch focus. - */ - virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; - - /* Registers input channels that may be used as targets for input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputChannel(const sp& inputChannel, - int32_t displayId) = 0; - - /* Registers input channels to be used to monitor input events. - * - * Each monitor must target a specific display and will only receive input events sent to that - * display. If the monitor is a gesture monitor, it will only receive pointer events on the - * targeted display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputMonitor(const sp& inputChannel, int32_t displayId, - bool gestureMonitor) = 0; - - /* Unregister input channels that will no longer receive input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; - - /* Allows an input monitor steal the current pointer stream away from normal input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t pilferPointers(const sp& token) = 0; -}; +class Connection; /* Dispatches events to input targets. Some functions of the input dispatcher, such as * identifying input targets, are controlled by a separate policy object. @@ -400,7 +71,7 @@ public: * * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. */ -class InputDispatcher : public InputDispatcherInterface { +class InputDispatcher : public android::InputDispatcherInterface { protected: virtual ~InputDispatcher(); @@ -442,376 +113,6 @@ public: virtual status_t pilferPointers(const sp& token) override; private: - struct InjectionState { - mutable int32_t refCount; - - int32_t injectorPid; - int32_t injectorUid; - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING - bool injectionIsAsync; // set to true if injection is not waiting for the result - int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress - - InjectionState(int32_t injectorPid, int32_t injectorUid); - void release(); - - private: - ~InjectionState(); - }; - - struct EventEntry { - enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; - - uint32_t sequenceNum; - mutable int32_t refCount; - int32_t type; - nsecs_t eventTime; - uint32_t policyFlags; - InjectionState* injectionState; - - bool dispatchInProgress; // initially false, set to true while dispatching - - inline bool isInjected() const { return injectionState != nullptr; } - - void release(); - - virtual void appendDescription(std::string& msg) const = 0; - - protected: - EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); - virtual ~EventEntry(); - void releaseInjectionState(); - }; - - struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~ConfigurationChangedEntry(); - }; - - struct DeviceResetEntry : EventEntry { - int32_t deviceId; - - DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~DeviceResetEntry(); - }; - - struct KeyEntry : EventEntry { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t repeatCount; - nsecs_t downTime; - - bool syntheticRepeat; // set to true for synthetic key repeats - - enum InterceptKeyResult { - INTERCEPT_KEY_RESULT_UNKNOWN, - INTERCEPT_KEY_RESULT_SKIP, - INTERCEPT_KEY_RESULT_CONTINUE, - INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, - }; - InterceptKeyResult interceptKeyResult; // set based on the interception result - nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER - - KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime); - virtual void appendDescription(std::string& msg) const; - void recycle(); - - protected: - virtual ~KeyEntry(); - }; - - struct MotionEntry : EventEntry { - nsecs_t eventTime; - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t actionButton; - int32_t flags; - int32_t metaState; - int32_t buttonState; - MotionClassification classification; - int32_t edgeFlags; - float xPrecision; - float yPrecision; - float xCursorPosition; - float yCursorPosition; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~MotionEntry(); - }; - - // Tracks the progress of dispatching a particular event to a particular connection. - struct DispatchEntry { - const uint32_t seq; // unique sequence number, never 0 - - EventEntry* eventEntry; // the event to dispatch - int32_t targetFlags; - float xOffset; - float yOffset; - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - nsecs_t deliveryTime; // time when the event was actually delivered - - // Set to the resolved action and flags when the event is enqueued. - int32_t resolvedAction; - int32_t resolvedFlags; - - DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, - float globalScaleFactor, float windowXScale, float windowYScale); - ~DispatchEntry(); - - inline bool hasForegroundTarget() const { - return targetFlags & InputTarget::FLAG_FOREGROUND; - } - - inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } - - private: - static volatile int32_t sNextSeqAtomic; - - static uint32_t nextSeq(); - }; - - // A command entry captures state and behavior for an action to be performed in the - // dispatch loop after the initial processing has taken place. It is essentially - // a kind of continuation used to postpone sensitive policy interactions to a point - // in the dispatch loop where it is safe to release the lock (generally after finishing - // the critical parts of the dispatch cycle). - // - // The special thing about commands is that they can voluntarily release and reacquire - // the dispatcher lock at will. Initially when the command starts running, the - // dispatcher lock is held. However, if the command needs to call into the policy to - // do some work, it can release the lock, do the work, then reacquire the lock again - // before returning. - // - // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch - // never calls into the policy while holding its lock. - // - // Commands are implicitly 'LockedInterruptible'. - struct CommandEntry; - typedef std::function Command; - - class Connection; - struct CommandEntry { - explicit CommandEntry(Command command); - ~CommandEntry(); - - Command command; - - // parameters for the command (usage varies by command) - sp connection; - nsecs_t eventTime; - KeyEntry* keyEntry; - sp inputApplicationHandle; - std::string reason; - int32_t userActivityEventType; - uint32_t seq; - bool handled; - sp inputChannel; - sp oldToken; - sp newToken; - }; - - /* Specifies which events are to be canceled and why. */ - struct CancelationOptions { - enum Mode { - CANCEL_ALL_EVENTS = 0, - CANCEL_POINTER_EVENTS = 1, - CANCEL_NON_POINTER_EVENTS = 2, - CANCEL_FALLBACK_EVENTS = 3, - }; - - // The criterion to use to determine which events should be canceled. - Mode mode; - - // Descriptive reason for the cancelation. - const char* reason; - - // The specific keycode of the key event to cancel, or nullopt to cancel any key event. - std::optional keyCode = std::nullopt; - - // The specific device id of events to cancel, or nullopt to cancel events from any device. - std::optional deviceId = std::nullopt; - - // The specific display id of events to cancel, or nullopt to cancel events on any display. - std::optional displayId = std::nullopt; - - CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} - }; - - /* Tracks dispatched key and motion event state so that cancelation events can be - * synthesized when events are dropped. */ - class InputState { - public: - InputState(); - ~InputState(); - - // Returns true if there is no state to be canceled. - bool isNeutral() const; - - // Returns true if the specified source is known to have received a hover enter - // motion event. - bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; - - // Records tracking information for a key event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); - - // Records tracking information for a motion event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); - - // Synthesizes cancelation events for the current state and resets the tracked state. - void synthesizeCancelationEvents(nsecs_t currentTime, std::vector& outEvents, - const CancelationOptions& options); - - // Clears the current state. - void clear(); - - // Copies pointer-related parts of the input state to another instance. - void copyPointerStateTo(InputState& other) const; - - // Gets the fallback key associated with a keycode. - // Returns -1 if none. - // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. - int32_t getFallbackKey(int32_t originalKeyCode); - - // Sets the fallback key for a particular keycode. - void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); - - // Removes the fallback key for a particular keycode. - void removeFallbackKey(int32_t originalKeyCode); - - inline const KeyedVector& getFallbackKeys() const { - return mFallbackKeys; - } - - private: - struct KeyMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t flags; - nsecs_t downTime; - uint32_t policyFlags; - }; - - struct MotionMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t flags; - float xPrecision; - float yPrecision; - float xCursorPosition; - float yCursorPosition; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - bool hovering; - uint32_t policyFlags; - - void setPointers(const MotionEntry* entry); - }; - - std::vector mKeyMementos; - std::vector mMotionMementos; - KeyedVector mFallbackKeys; - - ssize_t findKeyMemento(const KeyEntry* entry) const; - ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; - - void addKeyMemento(const KeyEntry* entry, int32_t flags); - void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); - - static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); - static bool shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options); - }; - - /* Manages the dispatch state associated with a single input channel. */ - class Connection : public RefBase { - protected: - virtual ~Connection(); - - public: - enum Status { - // Everything is peachy. - STATUS_NORMAL, - // An unrecoverable communication error has occurred. - STATUS_BROKEN, - // The input channel has been unregistered. - STATUS_ZOMBIE - }; - - Status status; - sp inputChannel; // never null - bool monitor; - InputPublisher inputPublisher; - InputState inputState; - - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; - - // Queue of events that need to be published to the connection. - std::deque outboundQueue; - - // Queue of events that have been published to the connection but that have not - // yet received a "finished" response from the application. - std::deque waitQueue; - - explicit Connection(const sp& inputChannel, bool monitor); - - inline const std::string getInputChannelName() const { return inputChannel->getName(); } - - const std::string getWindowName() const; - const char* getStatusLabel() const; - - std::deque::iterator findWaitQueueEntry(uint32_t seq); - }; - - struct Monitor { - sp inputChannel; // never null - - explicit Monitor(const sp& inputChannel); - }; enum DropReason { DROP_REASON_NOT_DROPPED = 0, @@ -823,7 +124,7 @@ private: }; sp mPolicy; - InputDispatcherConfiguration mConfig; + android::InputDispatcherConfiguration mConfig; std::mutex mLock; @@ -969,53 +270,6 @@ private: std::unordered_map> mFocusedWindowHandlesByDisplay GUARDED_BY(mLock); - // Focus tracking for touch. - struct TouchedWindow { - sp windowHandle; - int32_t targetFlags; - BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set - }; - - // For tracking the offsets we need to apply when adding gesture monitor targets. - struct TouchedMonitor { - Monitor monitor; - float xOffset = 0.f; - float yOffset = 0.f; - - explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); - }; - - struct TouchState { - bool down; - bool split; - int32_t deviceId; // id of the device that is currently down, others are rejected - uint32_t source; // source of the device that is current down, others are rejected - int32_t displayId; // id to the display that currently has a touch, others are rejected - std::vector windows; - - // This collects the portal windows that the touch has gone through. Each portal window - // targets a display (embedded display for most cases). With this info, we can add the - // monitoring channels of the displays touched. - std::vector> portalWindows; - - std::vector gestureMonitors; - - TouchState(); - ~TouchState(); - void reset(); - void copyFrom(const TouchState& other); - void addOrUpdateWindow(const sp& windowHandle, int32_t targetFlags, - BitSet32 pointerIds); - void addPortalWindow(const sp& windowHandle); - void addGestureMonitors(const std::vector& monitors); - void removeWindow(const sp& windowHandle); - void removeWindowByToken(const sp& token); - void filterNonAsIsTouchWindows(); - void filterNonMonitors(); - sp getFirstForegroundWindowHandle() const; - bool isSlippery() const; - }; - KeyedVector mTouchStatesByDisplay GUARDED_BY(mLock); TouchState mTempTouchState GUARDED_BY(mLock); @@ -1210,18 +464,6 @@ private: sp mReporter; }; -/* Enqueues and dispatches input events, endlessly. */ -class InputDispatcherThread : public Thread { -public: - explicit InputDispatcherThread(const sp& dispatcher); - ~InputDispatcherThread(); - -private: - virtual bool threadLoop(); - - sp mDispatcher; -}; - -} // namespace android +} // namespace android::inputdispatcher #endif // _UI_INPUT_DISPATCHER_H diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp new file mode 100644 index 0000000000..8d7fa7573b --- /dev/null +++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputDispatcherFactory.h" +#include "InputDispatcher.h" + +namespace android { + +sp createInputDispatcher( + const sp& policy) { + return new android::inputdispatcher::InputDispatcher(policy); +} + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp new file mode 100644 index 0000000000..18b1b8c10a --- /dev/null +++ b/services/inputflinger/dispatcher/InputDispatcherThread.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputDispatcherThread.h" + +#include "InputDispatcherInterface.h" + +namespace android { + +InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) + : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {} + +InputDispatcherThread::~InputDispatcherThread() {} + +bool InputDispatcherThread::threadLoop() { + mDispatcher->dispatchOnce(); + return true; +} + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp new file mode 100644 index 0000000000..c60700e369 --- /dev/null +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputState.h" + +namespace android::inputdispatcher { + +InputState::InputState() {} + +InputState::~InputState() {} + +bool InputState::isNeutral() const { + return mKeyMementos.empty() && mMotionMementos.empty(); +} + +bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const { + for (const MotionMemento& memento : mMotionMementos) { + if (memento.deviceId == deviceId && memento.source == source && + memento.displayId == displayId && memento.hovering) { + return true; + } + } + return false; +} + +bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) { + switch (action) { + case AKEY_EVENT_ACTION_UP: { + if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + for (size_t i = 0; i < mFallbackKeys.size();) { + if (mFallbackKeys.valueAt(i) == entry->keyCode) { + mFallbackKeys.removeItemsAt(i); + } else { + i += 1; + } + } + } + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + return true; + } + /* FIXME: We can't just drop the key up event because that prevents creating + * popup windows that are automatically shown when a key is held and then + * dismissed when the key is released. The problem is that the popup will + * not have received the original key down, so the key up will be considered + * to be inconsistent with its observed state. We could perhaps handle this + * by synthesizing a key down but that will cause other problems. + * + * So for now, allow inconsistent key up events to be dispatched. + * + #if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " + "keyCode=%d, scanCode=%d", + entry->deviceId, entry->source, entry->keyCode, entry->scanCode); + #endif + return false; + */ + return true; + } + + case AKEY_EVENT_ACTION_DOWN: { + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + } + addKeyMemento(entry, flags); + return true; + } + + default: + return true; + } +} + +bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) { + int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; + switch (actionMasked) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " + "displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, false /*hovering*/); + return true; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_MOVE: { + if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { + // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need + // to generate cancellation events for these since they're based in relative rather + // than absolute units. + return true; + } + + ssize_t index = findMotionMemento(entry, false /*hovering*/); + + if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { + // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all + // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. + // Any other value and we need to track the motion so we can send cancellation + // events for anything generating fallback events (e.g. DPad keys for joystick + // movements). + if (index >= 0) { + if (entry->pointerCoords[0].isEmpty()) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } else { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + } + } else if (!entry->pointerCoords[0].isEmpty()) { + addMotionMemento(entry, flags, false /*hovering*/); + } + + // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. + return true; + } + if (index >= 0) { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion pointer up/down or move event: " + "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " + "displayId=%" PRId32, + entry->deviceId, entry->source, entry->displayId); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, true /*hovering*/); + return true; + } + + default: + return true; + } +} + +ssize_t InputState::findKeyMemento(const KeyEntry* entry) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && + memento.scanCode == entry->scanCode) { + return i; + } + } + return -1; +} + +ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.hovering == hovering) { + return i; + } + } + return -1; +} + +void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { + KeyMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.metaState = entry->metaState; + memento.flags = flags; + memento.downTime = entry->downTime; + memento.policyFlags = entry->policyFlags; + mKeyMementos.push_back(memento); +} + +void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) { + MotionMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.flags = flags; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.xCursorPosition = entry->xCursorPosition; + memento.yCursorPosition = entry->yCursorPosition; + memento.downTime = entry->downTime; + memento.setPointers(entry); + memento.hovering = hovering; + memento.policyFlags = entry->policyFlags; + mMotionMementos.push_back(memento); +} + +void InputState::MotionMemento::setPointers(const MotionEntry* entry) { + pointerCount = entry->pointerCount; + for (uint32_t i = 0; i < entry->pointerCount; i++) { + pointerProperties[i].copyFrom(entry->pointerProperties[i]); + pointerCoords[i].copyFrom(entry->pointerCoords[i]); + } +} + +void InputState::synthesizeCancelationEvents(nsecs_t currentTime, + std::vector& outEvents, + const CancelationOptions& options) { + for (KeyMemento& memento : mKeyMementos) { + if (shouldCancelKey(memento, options)) { + outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, AKEY_EVENT_ACTION_UP, + memento.flags | AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, memento.metaState, + 0, memento.downTime)); + } + } + + for (const MotionMemento& memento : mMotionMementos) { + if (shouldCancelMotion(memento, options)) { + const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT + : AMOTION_EVENT_ACTION_CANCEL; + outEvents.push_back( + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, memento.pointerCount, + memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); + } + } +} + +void InputState::clear() { + mKeyMementos.clear(); + mMotionMementos.clear(); + mFallbackKeys.clear(); +} + +void InputState::copyPointerStateTo(InputState& other) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { + for (size_t j = 0; j < other.mMotionMementos.size();) { + const MotionMemento& otherMemento = other.mMotionMementos[j]; + if (memento.deviceId == otherMemento.deviceId && + memento.source == otherMemento.source && + memento.displayId == otherMemento.displayId) { + other.mMotionMementos.erase(other.mMotionMementos.begin() + j); + } else { + j += 1; + } + } + other.mMotionMementos.push_back(memento); + } + } +} + +int32_t InputState::getFallbackKey(int32_t originalKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + return index >= 0 ? mFallbackKeys.valueAt(index) : -1; +} + +void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + if (index >= 0) { + mFallbackKeys.replaceValueAt(index, fallbackKeyCode); + } else { + mFallbackKeys.add(originalKeyCode, fallbackKeyCode); + } +} + +void InputState::removeFallbackKey(int32_t originalKeyCode) { + mFallbackKeys.removeItem(originalKeyCode); +} + +bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) { + if (options.keyCode && memento.keyCode != options.keyCode.value()) { + return false; + } + + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return true; + case CancelationOptions::CANCEL_FALLBACK_EVENTS: + return memento.flags & AKEY_EVENT_FLAG_FALLBACK; + default: + return false; + } +} + +bool InputState::shouldCancelMotion(const MotionMemento& memento, + const CancelationOptions& options) { + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + return true; + case CancelationOptions::CANCEL_POINTER_EVENTS: + return memento.source & AINPUT_SOURCE_CLASS_POINTER; + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + default: + return false; + } +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h new file mode 100644 index 0000000000..bccef0fca3 --- /dev/null +++ b/services/inputflinger/dispatcher/InputState.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H + +#include "CancelationOptions.h" +#include "Entry.h" + +#include + +namespace android::inputdispatcher { + +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; + +/* Tracks dispatched key and motion event state so that cancellation events can be + * synthesized when events are dropped. */ +class InputState { +public: + InputState(); + ~InputState(); + + // Returns true if there is no state to be canceled. + bool isNeutral() const; + + // Returns true if the specified source is known to have received a hover enter + // motion event. + bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; + + // Records tracking information for a key event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); + + // Records tracking information for a motion event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); + + // Synthesizes cancelation events for the current state and resets the tracked state. + void synthesizeCancelationEvents(nsecs_t currentTime, std::vector& outEvents, + const CancelationOptions& options); + + // Clears the current state. + void clear(); + + // Copies pointer-related parts of the input state to another instance. + void copyPointerStateTo(InputState& other) const; + + // Gets the fallback key associated with a keycode. + // Returns -1 if none. + // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. + int32_t getFallbackKey(int32_t originalKeyCode); + + // Sets the fallback key for a particular keycode. + void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); + + // Removes the fallback key for a particular keycode. + void removeFallbackKey(int32_t originalKeyCode); + + inline const KeyedVector& getFallbackKeys() const { return mFallbackKeys; } + +private: + struct KeyMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t flags; + nsecs_t downTime; + uint32_t policyFlags; + }; + + struct MotionMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t flags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + bool hovering; + uint32_t policyFlags; + + void setPointers(const MotionEntry* entry); + }; + + std::vector mKeyMementos; + std::vector mMotionMementos; + KeyedVector mFallbackKeys; + + ssize_t findKeyMemento(const KeyEntry* entry) const; + ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + + void addKeyMemento(const KeyEntry* entry, int32_t flags); + void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + + static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); + static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp new file mode 100644 index 0000000000..80fa2cb995 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputTarget.h" + +#include +#include +#include + +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +std::string dispatchModeToString(int32_t dispatchMode) { + switch (dispatchMode) { + case InputTarget::FLAG_DISPATCH_AS_IS: + return "DISPATCH_AS_IS"; + case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: + return "DISPATCH_AS_OUTSIDE"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: + return "DISPATCH_AS_HOVER_ENTER"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: + return "DISPATCH_AS_HOVER_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: + return "DISPATCH_AS_SLIPPERY_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: + return "DISPATCH_AS_SLIPPERY_ENTER"; + } + return StringPrintf("%" PRId32, dispatchMode); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h new file mode 100644 index 0000000000..5acf92b1b1 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H + +#include +#include +#include + +namespace android::inputdispatcher { + +/* + * An input target specifies how an input event is to be dispatched to a particular window + * including the window's input channel, control flags, a timeout, and an X / Y offset to + * be added to input event coordinates to compensate for the absolute position of the + * window area. + */ +struct InputTarget { + enum { + /* This flag indicates that the event is being delivered to a foreground application. */ + FLAG_FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + FLAG_WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + FLAG_SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + FLAG_ZERO_COORDS = 1 << 3, + + /* This flag indicates that the event should be sent as is. + * Should always be set unless the event is to be transmuted. */ + FLAG_DISPATCH_AS_IS = 1 << 8, + + /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside + * of the area of this target and so should instead be delivered as an + * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ + FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + + /* This flag indicates that a hover sequence is starting in the given window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + + /* This flag indicates that a hover event happened outside of a window which handled + * previous hover events, signifying the end of the current hover sequence for that + * window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + + /* This flag indicates that the event should be canceled. + * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips + * outside of a window. */ + FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, + + /* This flag indicates that the event should be dispatched as an initial down. + * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips + * into a new window. */ + FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, + + /* Mask for all dispatch modes. */ + FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | + FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | + FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, + + }; + + // The input channel to be targeted. + sp inputChannel; + + // Flags for the input target. + int32_t flags; + + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset, yOffset; + + // Scaling factor to apply to MotionEvent as it is delivered. + // (ignored for KeyEvents) + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + + // The subset of pointer ids to include in motion events dispatched to this input target + // if FLAG_SPLIT is set. + BitSet32 pointerIds; +}; + +std::string dispatchModeToString(int32_t dispatchMode); + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp new file mode 100644 index 0000000000..289b0848bf --- /dev/null +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Monitor.h" + +namespace android::inputdispatcher { + +// --- Monitor --- +Monitor::Monitor(const sp& inputChannel) : inputChannel(inputChannel) {} + +// --- TouchedMonitor --- +TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset) + : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h new file mode 100644 index 0000000000..b67c9eb507 --- /dev/null +++ b/services/inputflinger/dispatcher/Monitor.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H +#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H + +#include + +namespace android::inputdispatcher { + +struct Monitor { + sp inputChannel; // never null + + explicit Monitor(const sp& inputChannel); +}; + +// For tracking the offsets we need to apply when adding gesture monitor targets. +struct TouchedMonitor { + Monitor monitor; + float xOffset = 0.f; + float yOffset = 0.f; + + explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp new file mode 100644 index 0000000000..18848a0c2f --- /dev/null +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "InputTarget.h" + +#include "TouchState.h" + +using android::InputWindowHandle; + +namespace android::inputdispatcher { + +TouchState::TouchState() + : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {} + +TouchState::~TouchState() {} + +void TouchState::reset() { + down = false; + split = false; + deviceId = -1; + source = 0; + displayId = ADISPLAY_ID_NONE; + windows.clear(); + portalWindows.clear(); + gestureMonitors.clear(); +} + +void TouchState::copyFrom(const TouchState& other) { + down = other.down; + split = other.split; + deviceId = other.deviceId; + source = other.source; + displayId = other.displayId; + windows = other.windows; + portalWindows = other.portalWindows; + gestureMonitors = other.gestureMonitors; +} + +void TouchState::addOrUpdateWindow(const sp& windowHandle, int32_t targetFlags, + BitSet32 pointerIds) { + if (targetFlags & InputTarget::FLAG_SPLIT) { + split = true; + } + + for (size_t i = 0; i < windows.size(); i++) { + TouchedWindow& touchedWindow = windows[i]; + if (touchedWindow.windowHandle == windowHandle) { + touchedWindow.targetFlags |= targetFlags; + if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; + } + touchedWindow.pointerIds.value |= pointerIds.value; + return; + } + } + + TouchedWindow touchedWindow; + touchedWindow.windowHandle = windowHandle; + touchedWindow.targetFlags = targetFlags; + touchedWindow.pointerIds = pointerIds; + windows.push_back(touchedWindow); +} + +void TouchState::addPortalWindow(const sp& windowHandle) { + size_t numWindows = portalWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + if (portalWindows[i] == windowHandle) { + return; + } + } + portalWindows.push_back(windowHandle); +} + +void TouchState::addGestureMonitors(const std::vector& newMonitors) { + const size_t newSize = gestureMonitors.size() + newMonitors.size(); + gestureMonitors.reserve(newSize); + gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors), + std::end(newMonitors)); +} + +void TouchState::removeWindow(const sp& windowHandle) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle == windowHandle) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void TouchState::removeWindowByToken(const sp& token) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle->getToken() == token) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void TouchState::filterNonAsIsTouchWindows() { + for (size_t i = 0; i < windows.size();) { + TouchedWindow& window = windows[i]; + if (window.targetFlags & + (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { + window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; + window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; + i += 1; + } else { + windows.erase(windows.begin() + i); + } + } +} + +void TouchState::filterNonMonitors() { + windows.clear(); + portalWindows.clear(); +} + +sp TouchState::getFirstForegroundWindowHandle() const { + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& window = windows[i]; + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + return window.windowHandle; + } + } + return nullptr; +} + +bool TouchState::isSlippery() const { + // Must have exactly one foreground window. + bool haveSlipperyForegroundWindow = false; + for (const TouchedWindow& window : windows) { + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (haveSlipperyForegroundWindow || + !(window.windowHandle->getInfo()->layoutParamsFlags & + InputWindowInfo::FLAG_SLIPPERY)) { + return false; + } + haveSlipperyForegroundWindow = true; + } + } + return haveSlipperyForegroundWindow; +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h new file mode 100644 index 0000000000..3e0e0eb897 --- /dev/null +++ b/services/inputflinger/dispatcher/TouchState.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H + +#include "Monitor.h" +#include "TouchedWindow.h" + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { + +struct TouchState { + bool down; + bool split; + int32_t deviceId; // id of the device that is currently down, others are rejected + uint32_t source; // source of the device that is current down, others are rejected + int32_t displayId; // id to the display that currently has a touch, others are rejected + std::vector windows; + + // This collects the portal windows that the touch has gone through. Each portal window + // targets a display (embedded display for most cases). With this info, we can add the + // monitoring channels of the displays touched. + std::vector> portalWindows; + + std::vector gestureMonitors; + + TouchState(); + ~TouchState(); + void reset(); + void copyFrom(const TouchState& other); + void addOrUpdateWindow(const sp& windowHandle, int32_t targetFlags, + BitSet32 pointerIds); + void addPortalWindow(const sp& windowHandle); + void addGestureMonitors(const std::vector& monitors); + void removeWindow(const sp& windowHandle); + void removeWindowByToken(const sp& token); + void filterNonAsIsTouchWindows(); + void filterNonMonitors(); + sp getFirstForegroundWindowHandle() const; + bool isSlippery() const; +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h new file mode 100644 index 0000000000..8713aa3f56 --- /dev/null +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H +#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { + +// Focus tracking for touch. +struct TouchedWindow { + sp windowHandle; + int32_t targetFlags; + BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h new file mode 100644 index 0000000000..00abf47cd2 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H + +#include + +namespace android { + +/* + * Input dispatcher configuration. + * + * Specifies various options that modify the behavior of the input dispatcher. + * The values provided here are merely defaults. The actual values will come from ViewConfiguration + * and are passed into the dispatcher during initialization. + */ +struct InputDispatcherConfiguration { + // The key repeat initial timeout. + nsecs_t keyRepeatTimeout; + + // The key repeat inter-key delay. + nsecs_t keyRepeatDelay; + + InputDispatcherConfiguration() + : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h new file mode 100644 index 0000000000..a359557f80 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H + +#include + +#include "InputDispatcherInterface.h" +#include "InputDispatcherPolicyInterface.h" + +namespace android { + +// This factory method is used to encapsulate implementation details in internal header files. +sp createInputDispatcher( + const sp& policy); + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h new file mode 100644 index 0000000000..9329ca664e --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H + +#include +#include + +namespace android { + +class InputApplicationHandle; +class InputChannel; +class InputWindowHandle; + +/* + * Constants used to report the outcome of input event injection. + */ +enum { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + INPUT_EVENT_INJECTION_PENDING = -1, + + /* Injection succeeded. */ + INPUT_EVENT_INJECTION_SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + INPUT_EVENT_INJECTION_FAILED = 2, + + /* Injection failed due to a timeout. */ + INPUT_EVENT_INJECTION_TIMED_OUT = 3 +}; + +/* Notifies the system about input events generated by the input reader. + * The dispatcher is expected to be mostly asynchronous. */ +class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { +protected: + InputDispatcherInterface() {} + virtual ~InputDispatcherInterface() {} + +public: + /* Dumps the state of the input dispatcher. + * + * This method may be called on any thread (usually by the input manager). */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + /* Runs a single iteration of the dispatch loop. + * Nominally processes one queued event, a timeout, or a response from an input consumer. + * + * This method should only be called on the input dispatcher thread. + */ + virtual void dispatchOnce() = 0; + + /* Injects an input event and optionally waits for sync. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + uint32_t policyFlags) = 0; + + /* Sets the list of input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputWindows( + const std::vector >& inputWindowHandles, int32_t displayId, + const sp& setInputWindowsListener = nullptr) = 0; + + /* Sets the focused application on the given display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedApplication( + int32_t displayId, const sp& inputApplicationHandle) = 0; + + /* Sets the focused display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedDisplay(int32_t displayId) = 0; + + /* Sets the input dispatching mode. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; + + /* Sets whether input event filtering is enabled. + * When enabled, incoming input events are sent to the policy's filterInputEvent + * method instead of being dispatched. The filter is expected to use + * injectInputEvent to inject the events it would like to have dispatched. + * It should include POLICY_FLAG_FILTERED in the policy flags during injection. + */ + virtual void setInputFilterEnabled(bool enabled) = 0; + + /* Transfers touch focus from one window to another window. + * + * Returns true on success. False if the window did not actually have touch focus. + */ + virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) = 0; + + /* Registers input channels that may be used as targets for input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputChannel(const sp& inputChannel, + int32_t displayId) = 0; + + /* Registers input channels to be used to monitor input events. + * + * Each monitor must target a specific display and will only receive input events sent to that + * display. If the monitor is a gesture monitor, it will only receive pointer events on the + * targeted display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputMonitor(const sp& inputChannel, int32_t displayId, + bool gestureMonitor) = 0; + + /* Unregister input channels that will no longer receive input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t unregisterInputChannel(const sp& inputChannel) = 0; + + /* Allows an input monitor steal the current pointer stream away from normal input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t pilferPointers(const sp& token) = 0; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h new file mode 100644 index 0000000000..4214488f04 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H + +#include "InputDispatcherConfiguration.h" + +#include +#include +#include + +namespace android { + +class InputApplicationHandle; + +/* + * Input dispatcher policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputDispatcherPolicyInterface : public virtual RefBase { +protected: + InputDispatcherPolicyInterface() {} + virtual ~InputDispatcherPolicyInterface() {} + +public: + /* Notifies the system that a configuration change has occurred. */ + virtual void notifyConfigurationChanged(nsecs_t when) = 0; + + /* Notifies the system that an application is not responding. + * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ + virtual nsecs_t notifyANR(const sp& inputApplicationHandle, + const sp& token, const std::string& reason) = 0; + + /* Notifies the system that an input channel is unrecoverably broken. */ + virtual void notifyInputChannelBroken(const sp& token) = 0; + virtual void notifyFocusChanged(const sp& oldToken, const sp& newToken) = 0; + + /* Gets the input dispatcher configuration. */ + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; + + /* Filters an input event. + * Return true to dispatch the event unmodified, false to consume the event. + * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED + * to injectInputEvent. + */ + virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; + + /* Intercepts a key event immediately before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; + + /* Intercepts a touch, trackball or other motion event before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) = 0; + + /* Allows the policy a chance to intercept a key before dispatching. */ + virtual nsecs_t interceptKeyBeforeDispatching(const sp& token, + const KeyEvent* keyEvent, + uint32_t policyFlags) = 0; + + /* Allows the policy a chance to perform default processing for an unhandled key. + * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ + virtual bool dispatchUnhandledKey(const sp& token, const KeyEvent* keyEvent, + uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; + + /* Notifies the policy about switch events. + */ + virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) = 0; + + /* Poke user activity for an event dispatched to a window. */ + virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; + + /* Checks whether a given application pid/uid has permission to inject input events + * into other applications. + * + * This method is special in that its implementation promises to be non-reentrant and + * is safe to call while holding other locks. (Most other methods make no such guarantees!) + */ + virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, + int32_t injectorUid) = 0; + + /* Notifies the policy that a pointer down event has occurred outside the current focused + * window. + * + * The touchedToken passed as an argument is the window that received the input event. + */ + virtual void onPointerDownOutsideFocus(const sp& touchedToken) = 0; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h new file mode 100644 index 0000000000..2604959656 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherThread.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H + +#include +#include + +namespace android { + +class InputDispatcherInterface; + +/* Enqueues and dispatches input events, endlessly. */ +class InputDispatcherThread : public Thread { +public: + explicit InputDispatcherThread(const sp& dispatcher); + ~InputDispatcherThread(); + +private: + virtual bool threadLoop(); + + sp mDispatcher; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/include/InputReporterInterface.h deleted file mode 100644 index 906d7f25f2..0000000000 --- a/services/inputflinger/include/InputReporterInterface.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_REPORTER_INTERFACE_H -#define _UI_INPUT_REPORTER_INTERFACE_H - -#include - -namespace android { - -/* - * The interface used by the InputDispatcher to report information about input events after - * it is sent to the application, such as if a key is unhandled or dropped. - */ -class InputReporterInterface : public virtual RefBase { -protected: - virtual ~InputReporterInterface() { } - -public: - // Report a key that was not handled by the system or apps. - // A key event is unhandled if: - // - The event was not handled and there is no fallback key; or - // - The event was not handled and it has a fallback key, - // but the fallback key was not handled. - virtual void reportUnhandledKey(uint32_t sequenceNum) = 0; - - // Report a key that was dropped by InputDispatcher. - // A key can be dropped for several reasons. See the enum - // InputDispatcher::DropReason for details. - virtual void reportDroppedKey(uint32_t sequenceNum) = 0; -}; - -/* - * Factory method for InputReporter. - */ -sp createInputReporter(); - -} // namespace android - -#endif // _UI_INPUT_REPORTER_INTERFACE_H diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp new file mode 100644 index 0000000000..5956fb0794 --- /dev/null +++ b/services/inputflinger/reporter/Android.bp @@ -0,0 +1,41 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_headers { + name: "libinputreporter_headers", + export_include_dirs: ["."], +} + +cc_library_shared { + name: "libinputreporter", + defaults: ["inputflinger_defaults"], + + srcs: [ + "InputReporter.cpp", + ], + + shared_libs: [ + "liblog", + "libutils", + ], + + header_libs: [ + "libinputflinger_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], +} + diff --git a/services/inputflinger/reporter/InputReporter.cpp b/services/inputflinger/reporter/InputReporter.cpp new file mode 100644 index 0000000000..b591d3f909 --- /dev/null +++ b/services/inputflinger/reporter/InputReporter.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputReporterInterface.h" + +namespace android { + +// --- InputReporter --- + +class InputReporter : public InputReporterInterface { +public: + void reportUnhandledKey(uint32_t sequenceNum) override; + void reportDroppedKey(uint32_t sequenceNum) override; +}; + +void InputReporter::reportUnhandledKey(uint32_t sequenceNum) { + // do nothing +} + +void InputReporter::reportDroppedKey(uint32_t sequenceNum) { + // do nothing +} + +sp createInputReporter() { + return new InputReporter(); +} + +} // namespace android diff --git a/services/inputflinger/reporter/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h new file mode 100644 index 0000000000..e5d360609f --- /dev/null +++ b/services/inputflinger/reporter/InputReporterInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_REPORTER_INTERFACE_H +#define _UI_INPUT_REPORTER_INTERFACE_H + +#include + +namespace android { + +/* + * The interface used by the InputDispatcher to report information about input events after + * it is sent to the application, such as if a key is unhandled or dropped. + */ +class InputReporterInterface : public virtual RefBase { +protected: + virtual ~InputReporterInterface() {} + +public: + // Report a key that was not handled by the system or apps. + // A key event is unhandled if: + // - The event was not handled and there is no fallback key; or + // - The event was not handled and it has a fallback key, + // but the fallback key was not handled. + virtual void reportUnhandledKey(uint32_t sequenceNum) = 0; + + // Report a key that was dropped by InputDispatcher. + // A key can be dropped for several reasons. See the enum + // InputDispatcher::DropReason for details. + virtual void reportDroppedKey(uint32_t sequenceNum) = 0; +}; + +/* + * Factory method for InputReporter. + */ +sp createInputReporter(); + +} // namespace android + +#endif // _UI_INPUT_REPORTER_INTERFACE_H diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 8474b1cb8a..aa98ef78f5 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,12 +16,14 @@ #include "../dispatcher/InputDispatcher.h" +#include + #include #include #include -namespace android { +namespace android::inputdispatcher { // An arbitrary time value. static const nsecs_t ARBITRARY_TIME = 1234; @@ -1082,4 +1084,4 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownEquals(nullptr); } -} // namespace android +} // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From da7c00cc01694df1874c4c71ece4aa0364e4d778 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 29 Aug 2019 14:12:42 -0700 Subject: Create inputreader directory In order to start the work to split InputReader.cpp into multiple files, we first create a reader directory to hold the source files for libinputreader. The affected files are also reformatted with clang-format. Bug: 140139676 Test: libinputflinger and libinputflinger_tests builds successfully for crosshatch Change-Id: I62b6b873e2a03e61655eb6214183c984306dcfdb --- services/inputflinger/Android.bp | 32 - services/inputflinger/EventHub.cpp | 1949 ----- services/inputflinger/EventHub.h | 484 -- services/inputflinger/InputManager.h | 2 - services/inputflinger/InputReader.cpp | 7575 -------------------- services/inputflinger/InputReader.h | 1743 ----- services/inputflinger/InputReaderFactory.cpp | 28 - services/inputflinger/TouchVideoDevice.cpp | 250 - services/inputflinger/TouchVideoDevice.h | 125 - services/inputflinger/reader/Android.bp | 51 + services/inputflinger/reader/EventHub.cpp | 1945 +++++ services/inputflinger/reader/InputReader.cpp | 7461 +++++++++++++++++++ .../inputflinger/reader/InputReaderFactory.cpp | 27 + services/inputflinger/reader/TouchVideoDevice.cpp | 254 + services/inputflinger/reader/include/EventHub.h | 481 ++ services/inputflinger/reader/include/InputReader.h | 1706 +++++ .../inputflinger/reader/include/TouchVideoDevice.h | 125 + services/inputflinger/tests/Android.bp | 3 + services/inputflinger/tests/InputReader_test.cpp | 2 +- 19 files changed, 12054 insertions(+), 12189 deletions(-) delete mode 100644 services/inputflinger/EventHub.cpp delete mode 100644 services/inputflinger/EventHub.h delete mode 100644 services/inputflinger/InputReader.cpp delete mode 100644 services/inputflinger/InputReader.h delete mode 100644 services/inputflinger/InputReaderFactory.cpp delete mode 100644 services/inputflinger/TouchVideoDevice.cpp delete mode 100644 services/inputflinger/TouchVideoDevice.h create mode 100644 services/inputflinger/reader/Android.bp create mode 100644 services/inputflinger/reader/EventHub.cpp create mode 100644 services/inputflinger/reader/InputReader.cpp create mode 100644 services/inputflinger/reader/InputReaderFactory.cpp create mode 100644 services/inputflinger/reader/TouchVideoDevice.cpp create mode 100644 services/inputflinger/reader/include/EventHub.h create mode 100644 services/inputflinger/reader/include/InputReader.h create mode 100644 services/inputflinger/reader/include/TouchVideoDevice.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 32dab17420..11578c393e 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -75,38 +75,6 @@ cc_library_headers { export_header_lib_headers: ["libinputreporter_headers"], } -cc_library_shared { - name: "libinputreader", - defaults: ["inputflinger_defaults"], - - srcs: [ - "EventHub.cpp", - "InputReader.cpp", - "InputReaderFactory.cpp", - "TouchVideoDevice.cpp", - ], - - shared_libs: [ - "libbase", - "libinputflinger_base", - "libcrypto", - "libcutils", - "libinput", - "liblog", - "libui", - "libutils", - "libhardware_legacy", - ], - - header_libs: [ - "libinputflinger_headers", - ], - - export_header_lib_headers: [ - "libinputflinger_headers", - ], -} - cc_library_shared { name: "libinputflinger_base", defaults: ["inputflinger_defaults"], diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp deleted file mode 100644 index af023148cc..0000000000 --- a/services/inputflinger/EventHub.cpp +++ /dev/null @@ -1,1949 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_TAG "EventHub" - -// #define LOG_NDEBUG 0 - -#include "EventHub.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* this macro is used to tell if "bit" is set in "array" - * it selects a byte from the array, and does a boolean AND - * operation with a byte that only has the relevant bit set. - * eg. to check for the 12th bit, we do (array[1] & 1<<4) - */ -#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8))) - -/* this macro computes the number of bytes needed to represent a bit array of the specified size */ -#define sizeof_bit_array(bits) (((bits) + 7) / 8) - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " - -using android::base::StringPrintf; - -namespace android { - -static constexpr bool DEBUG = false; - -static const char *WAKE_LOCK_ID = "KeyEvents"; -static const char *DEVICE_PATH = "/dev/input"; -// v4l2 devices go directly into /dev -static const char *VIDEO_DEVICE_PATH = "/dev"; - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static std::string sha1(const std::string& in) { - SHA_CTX ctx; - SHA1_Init(&ctx); - SHA1_Update(&ctx, reinterpret_cast(in.c_str()), in.size()); - u_char digest[SHA_DIGEST_LENGTH]; - SHA1_Final(digest, &ctx); - - std::string out; - for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { - out += StringPrintf("%02x", digest[i]); - } - return out; -} - -/** - * Return true if name matches "v4l-touch*" - */ -static bool isV4lTouchNode(const char* name) { - return strstr(name, "v4l-touch") == name; -} - -/** - * Returns true if V4L devices should be scanned. - * - * The system property ro.input.video_enabled can be used to control whether - * EventHub scans and opens V4L devices. As V4L does not support multiple - * clients, EventHub effectively blocks access to these devices when it opens - * them. - * - * Setting this to "false" would prevent any video devices from being discovered and - * associated with input devices. - * - * This property can be used as follows: - * 1. To turn off features that are dependent on video device presence. - * 2. During testing and development, to allow other clients to read video devices - * directly from /dev. - */ -static bool isV4lScanningEnabled() { - return property_get_bool("ro.input.video_enabled", true /* default_value */); -} - -static nsecs_t processEventTimestamp(const struct input_event& event) { - // Use the time specified in the event instead of the current time - // so that downstream code can get more accurate estimates of - // event dispatch latency from the time the event is enqueued onto - // the evdev client buffer. - // - // The event's timestamp fortuitously uses the same monotonic clock - // time base as the rest of Android. The kernel event device driver - // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). - // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere - // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a - // system call that also queries ktime_get_ts(). - - const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) + - microseconds_to_nanoseconds(event.time.tv_usec); - return inputEventTime; -} - -// --- Global Functions --- - -uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { - // Touch devices get dibs on touch-related axes. - if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) { - switch (axis) { - case ABS_X: - case ABS_Y: - case ABS_PRESSURE: - case ABS_TOOL_WIDTH: - case ABS_DISTANCE: - case ABS_TILT_X: - case ABS_TILT_Y: - case ABS_MT_SLOT: - case ABS_MT_TOUCH_MAJOR: - case ABS_MT_TOUCH_MINOR: - case ABS_MT_WIDTH_MAJOR: - case ABS_MT_WIDTH_MINOR: - case ABS_MT_ORIENTATION: - case ABS_MT_POSITION_X: - case ABS_MT_POSITION_Y: - case ABS_MT_TOOL_TYPE: - case ABS_MT_BLOB_ID: - case ABS_MT_TRACKING_ID: - case ABS_MT_PRESSURE: - case ABS_MT_DISTANCE: - return INPUT_DEVICE_CLASS_TOUCH; - } - } - - // External stylus gets the pressure axis - if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - if (axis == ABS_PRESSURE) { - return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; - } - } - - // Joystick devices get the rest. - return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK; -} - -// --- EventHub::Device --- - -EventHub::Device::Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier) : - next(nullptr), - fd(fd), id(id), path(path), identifier(identifier), - classes(0), configuration(nullptr), virtualKeyMap(nullptr), - ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0), - enabled(true), isVirtual(fd < 0) { - memset(keyBitmask, 0, sizeof(keyBitmask)); - memset(absBitmask, 0, sizeof(absBitmask)); - memset(relBitmask, 0, sizeof(relBitmask)); - memset(swBitmask, 0, sizeof(swBitmask)); - memset(ledBitmask, 0, sizeof(ledBitmask)); - memset(ffBitmask, 0, sizeof(ffBitmask)); - memset(propBitmask, 0, sizeof(propBitmask)); -} - -EventHub::Device::~Device() { - close(); - delete configuration; -} - -void EventHub::Device::close() { - if (fd >= 0) { - ::close(fd); - fd = -1; - } -} - -status_t EventHub::Device::enable() { - fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); - if(fd < 0) { - ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno)); - return -errno; - } - enabled = true; - return OK; -} - -status_t EventHub::Device::disable() { - close(); - enabled = false; - return OK; -} - -bool EventHub::Device::hasValidFd() { - return !isVirtual && enabled; -} - -// --- EventHub --- - -const int EventHub::EPOLL_MAX_EVENTS; - -EventHub::EventHub(void) : - mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), - mOpeningDevices(nullptr), mClosingDevices(nullptr), - mNeedToSendFinishedDeviceScan(false), - mNeedToReopenDevices(false), mNeedToScanDevices(true), - mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - - mEpollFd = epoll_create1(EPOLL_CLOEXEC); - LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); - - mINotifyFd = inotify_init(); - mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); - LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", - DEVICE_PATH, strerror(errno)); - if (isV4lScanningEnabled()) { - mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE); - LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s", - VIDEO_DEVICE_PATH, strerror(errno)); - } else { - mVideoWd = -1; - ALOGI("Video device scanning disabled"); - } - - struct epoll_event eventItem; - memset(&eventItem, 0, sizeof(eventItem)); - eventItem.events = EPOLLIN; - eventItem.data.fd = mINotifyFd; - int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); - - int wakeFds[2]; - result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); - - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); - - eventItem.data.fd = mWakeReadPipeFd; - result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", - errno); -} - -EventHub::~EventHub(void) { - closeAllDevicesLocked(); - - while (mClosingDevices) { - Device* device = mClosingDevices; - mClosingDevices = device->next; - delete device; - } - - ::close(mEpollFd); - ::close(mINotifyFd); - ::close(mWakeReadPipeFd); - ::close(mWakeWritePipeFd); - - release_wake_lock(WAKE_LOCK_ID); -} - -InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return InputDeviceIdentifier(); - return device->identifier; -} - -uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return 0; - return device->classes; -} - -int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return 0; - return device->controllerNumber; -} - -void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device && device->configuration) { - *outConfiguration = *device->configuration; - } else { - outConfiguration->clear(); - } -} - -status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - outAxisInfo->clear(); - - if (axis >= 0 && axis <= ABS_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { - struct input_absinfo info; - if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.c_str(), device->fd, errno); - return -errno; - } - - if (info.minimum != info.maximum) { - outAxisInfo->valid = true; - outAxisInfo->minValue = info.minimum; - outAxisInfo->maxValue = info.maximum; - outAxisInfo->flat = info.flat; - outAxisInfo->fuzz = info.fuzz; - outAxisInfo->resolution = info.resolution; - } - return OK; - } - } - return -1; -} - -bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { - if (axis >= 0 && axis <= REL_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device) { - return test_bit(axis, device->relBitmask); - } - } - return false; -} - -bool EventHub::hasInputProperty(int32_t deviceId, int property) const { - if (property >= 0 && property <= INPUT_PROP_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device) { - return test_bit(property, device->propBitmask); - } - } - return false; -} - -int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { - if (scanCode >= 0 && scanCode <= KEY_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) { - uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; - memset(keyState, 0, sizeof(keyState)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { - return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; - } - } - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); - if (scanCodes.size() != 0) { - uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; - memset(keyState, 0, sizeof(keyState)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { - for (size_t i = 0; i < scanCodes.size(); i++) { - int32_t sc = scanCodes[i]; - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) { - return AKEY_STATE_DOWN; - } - } - return AKEY_STATE_UP; - } - } - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { - if (sw >= 0 && sw <= SW_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) { - uint8_t swState[sizeof_bit_array(SW_MAX + 1)]; - memset(swState, 0, sizeof(swState)); - if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) { - return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; - } - } - } - return AKEY_STATE_UNKNOWN; -} - -status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const { - *outValue = 0; - - if (axis >= 0 && axis <= ABS_MAX) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { - struct input_absinfo info; - if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.c_str(), device->fd, errno); - return -errno; - } - - *outValue = info.value; - return OK; - } - } - return -1; -} - -bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (device && device->keyMap.haveKeyLayout()) { - std::vector scanCodes; - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey( - keyCodes[codeIndex], &scanCodes); - if (! err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], device->keyBitmask)) { - outFlags[codeIndex] = 1; - break; - } - } - } - } - return true; - } - return false; -} - -status_t EventHub::mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - status_t status = NAME_NOT_FOUND; - - if (device) { - // Check the key character map first. - sp kcm = device->getKeyCharacterMap(); - if (kcm != nullptr) { - if (!kcm->mapKey(scanCode, usageCode, outKeycode)) { - *outFlags = 0; - status = NO_ERROR; - } - } - - // Check the key layout next. - if (status != NO_ERROR && device->keyMap.haveKeyLayout()) { - if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) { - status = NO_ERROR; - } - } - - if (status == NO_ERROR) { - if (kcm != nullptr) { - kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState); - } else { - *outMetaState = metaState; - } - } - } - - if (status != NO_ERROR) { - *outKeycode = 0; - *outFlags = 0; - *outMetaState = metaState; - } - - return status; -} - -status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - - if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - - return NAME_NOT_FOUND; -} - -void EventHub::setExcludedDevices(const std::vector& devices) { - AutoMutex _l(mLock); - - mExcludedDevices = devices; -} - -bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device && scanCode >= 0 && scanCode <= KEY_MAX) { - if (test_bit(scanCode, device->keyBitmask)) { - return true; - } - } - return false; -} - -bool EventHub::hasLed(int32_t deviceId, int32_t led) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - int32_t sc; - if (device && mapLed(device, led, &sc) == NO_ERROR) { - if (test_bit(sc, device->ledBitmask)) { - return true; - } - } - return false; -} - -void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - setLedStateLocked(device, led, on); -} - -void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) { - int32_t sc; - if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) { - struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; - ev.type = EV_LED; - ev.code = sc; - ev.value = on ? 1 : 0; - - ssize_t nWrite; - do { - nWrite = write(device->fd, &ev, sizeof(struct input_event)); - } while (nWrite == -1 && errno == EINTR); - } -} - -void EventHub::getVirtualKeyDefinitions(int32_t deviceId, - std::vector& outVirtualKeys) const { - outVirtualKeys.clear(); - - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device && device->virtualKeyMap) { - const std::vector virtualKeys = - device->virtualKeyMap->getVirtualKeys(); - outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end()); - } -} - -sp EventHub::getKeyCharacterMap(int32_t deviceId) const { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device) { - return device->getKeyCharacterMap(); - } - return nullptr; -} - -bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, - const sp& map) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device) { - if (map != device->overlayKeyMap) { - device->overlayKeyMap = map; - device->combinedKeyMap = KeyCharacterMap::combine( - device->keyMap.keyCharacterMap, map); - return true; - } - } - return false; -} - -static std::string generateDescriptor(InputDeviceIdentifier& identifier) { - std::string rawDescriptor; - rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, - identifier.product); - // TODO add handling for USB devices to not uniqueify kbs that show up twice - if (!identifier.uniqueId.empty()) { - rawDescriptor += "uniqueId:"; - rawDescriptor += identifier.uniqueId; - } else if (identifier.nonce != 0) { - rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce); - } - - if (identifier.vendor == 0 && identifier.product == 0) { - // If we don't know the vendor and product id, then the device is probably - // built-in so we need to rely on other information to uniquely identify - // the input device. Usually we try to avoid relying on the device name or - // location but for built-in input device, they are unlikely to ever change. - if (!identifier.name.empty()) { - rawDescriptor += "name:"; - rawDescriptor += identifier.name; - } else if (!identifier.location.empty()) { - rawDescriptor += "location:"; - rawDescriptor += identifier.location; - } - } - identifier.descriptor = sha1(rawDescriptor); - return rawDescriptor; -} - -void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { - // Compute a device descriptor that uniquely identifies the device. - // The descriptor is assumed to be a stable identifier. Its value should not - // change between reboots, reconnections, firmware updates or new releases - // of Android. In practice we sometimes get devices that cannot be uniquely - // identified. In this case we enforce uniqueness between connected devices. - // Ideally, we also want the descriptor to be short and relatively opaque. - - identifier.nonce = 0; - std::string rawDescriptor = generateDescriptor(identifier); - if (identifier.uniqueId.empty()) { - // If it didn't have a unique id check for conflicts and enforce - // uniqueness if necessary. - while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { - identifier.nonce++; - rawDescriptor = generateDescriptor(identifier); - } - } - ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), - identifier.descriptor.c_str()); -} - -void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd()) { - ff_effect effect; - memset(&effect, 0, sizeof(effect)); - effect.type = FF_RUMBLE; - effect.id = device->ffEffectId; - effect.u.rumble.strong_magnitude = 0xc000; - effect.u.rumble.weak_magnitude = 0xc000; - effect.replay.length = (duration + 999999LL) / 1000000LL; - effect.replay.delay = 0; - if (ioctl(device->fd, EVIOCSFF, &effect)) { - ALOGW("Could not upload force feedback effect to device %s due to error %d.", - device->identifier.name.c_str(), errno); - return; - } - device->ffEffectId = effect.id; - - struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; - ev.type = EV_FF; - ev.code = device->ffEffectId; - ev.value = 1; - if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { - ALOGW("Could not start force feedback effect on device %s due to error %d.", - device->identifier.name.c_str(), errno); - return; - } - device->ffEffectPlaying = true; - } -} - -void EventHub::cancelVibrate(int32_t deviceId) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd()) { - if (device->ffEffectPlaying) { - device->ffEffectPlaying = false; - - struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; - ev.type = EV_FF; - ev.code = device->ffEffectId; - ev.value = 0; - if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { - ALOGW("Could not stop force feedback effect on device %s due to error %d.", - device->identifier.name.c_str(), errno); - return; - } - } - } -} - -EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { - size_t size = mDevices.size(); - for (size_t i = 0; i < size; i++) { - Device* device = mDevices.valueAt(i); - if (descriptor == device->identifier.descriptor) { - return device; - } - } - return nullptr; -} - -EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { - if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) { - deviceId = mBuiltInKeyboardId; - } - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : NULL; -} - -EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const { - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); - if (device->path == devicePath) { - return device; - } - } - return nullptr; -} - -/** - * The file descriptor could be either input device, or a video device (associated with a - * specific input device). Check both cases here, and return the device that this event - * belongs to. Caller can compare the fd's once more to determine event type. - * Looks through all input devices, and only attached video devices. Unattached video - * devices are ignored. - */ -EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const { - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); - if (device->fd == fd) { - // This is an input device event - return device; - } - if (device->videoDevice && device->videoDevice->getFd() == fd) { - // This is a video device event - return device; - } - } - // We do not check mUnattachedVideoDevices here because they should not participate in epoll, - // and therefore should never be looked up by fd. - return nullptr; -} - -size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { - ALOG_ASSERT(bufferSize >= 1); - - AutoMutex _l(mLock); - - struct input_event readBuffer[bufferSize]; - - RawEvent* event = buffer; - size_t capacity = bufferSize; - bool awoken = false; - for (;;) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - // Reopen input devices if needed. - if (mNeedToReopenDevices) { - mNeedToReopenDevices = false; - - ALOGI("Reopening all input devices due to a configuration change."); - - closeAllDevicesLocked(); - mNeedToScanDevices = true; - break; // return to the caller before we actually rescan - } - - // Report any devices that had last been added/removed. - while (mClosingDevices) { - Device* device = mClosingDevices; - ALOGV("Reporting device closed: id=%d, name=%s\n", - device->id, device->path.c_str()); - mClosingDevices = device->next; - event->when = now; - event->deviceId = (device->id == mBuiltInKeyboardId) ? - ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id; - event->type = DEVICE_REMOVED; - event += 1; - delete device; - mNeedToSendFinishedDeviceScan = true; - if (--capacity == 0) { - break; - } - } - - if (mNeedToScanDevices) { - mNeedToScanDevices = false; - scanDevicesLocked(); - mNeedToSendFinishedDeviceScan = true; - } - - while (mOpeningDevices != nullptr) { - Device* device = mOpeningDevices; - ALOGV("Reporting device opened: id=%d, name=%s\n", - device->id, device->path.c_str()); - mOpeningDevices = device->next; - event->when = now; - event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; - event->type = DEVICE_ADDED; - event += 1; - mNeedToSendFinishedDeviceScan = true; - if (--capacity == 0) { - break; - } - } - - if (mNeedToSendFinishedDeviceScan) { - mNeedToSendFinishedDeviceScan = false; - event->when = now; - event->type = FINISHED_DEVICE_SCAN; - event += 1; - if (--capacity == 0) { - break; - } - } - - // Grab the next input event. - bool deviceChanged = false; - while (mPendingEventIndex < mPendingEventCount) { - const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; - if (eventItem.data.fd == mINotifyFd) { - if (eventItem.events & EPOLLIN) { - mPendingINotify = true; - } else { - ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); - } - continue; - } - - if (eventItem.data.fd == mWakeReadPipeFd) { - if (eventItem.events & EPOLLIN) { - ALOGV("awoken after wake()"); - awoken = true; - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); - } else { - ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", - eventItem.events); - } - continue; - } - - Device* device = getDeviceByFdLocked(eventItem.data.fd); - if (!device) { - ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", - eventItem.events, eventItem.data.fd); - ALOG_ASSERT(!DEBUG); - continue; - } - if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) { - if (eventItem.events & EPOLLIN) { - size_t numFrames = device->videoDevice->readAndQueueFrames(); - if (numFrames == 0) { - ALOGE("Received epoll event for video device %s, but could not read frame", - device->videoDevice->getName().c_str()); - } - } else if (eventItem.events & EPOLLHUP) { - // TODO(b/121395353) - consider adding EPOLLRDHUP - ALOGI("Removing video device %s due to epoll hang-up event.", - device->videoDevice->getName().c_str()); - unregisterVideoDeviceFromEpollLocked(*device->videoDevice); - device->videoDevice = nullptr; - } else { - ALOGW("Received unexpected epoll event 0x%08x for device %s.", - eventItem.events, device->videoDevice->getName().c_str()); - ALOG_ASSERT(!DEBUG); - } - continue; - } - // This must be an input event - if (eventItem.events & EPOLLIN) { - int32_t readSize = read(device->fd, readBuffer, - sizeof(struct input_event) * capacity); - if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { - // Device was removed before INotify noticed. - ALOGW("could not get event, removed? (fd: %d size: %" PRId32 - " bufferSize: %zu capacity: %zu errno: %d)\n", - device->fd, readSize, bufferSize, capacity, errno); - deviceChanged = true; - closeDeviceLocked(device); - } else if (readSize < 0) { - if (errno != EAGAIN && errno != EINTR) { - ALOGW("could not get event (errno=%d)", errno); - } - } else if ((readSize % sizeof(struct input_event)) != 0) { - ALOGE("could not get event (wrong size: %d)", readSize); - } else { - int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; - - size_t count = size_t(readSize) / sizeof(struct input_event); - for (size_t i = 0; i < count; i++) { - struct input_event& iev = readBuffer[i]; - event->when = processEventTimestamp(iev); - event->deviceId = deviceId; - event->type = iev.type; - event->code = iev.code; - event->value = iev.value; - event += 1; - capacity -= 1; - } - if (capacity == 0) { - // The result buffer is full. Reset the pending event index - // so we will try to read the device again on the next iteration. - mPendingEventIndex -= 1; - break; - } - } - } else if (eventItem.events & EPOLLHUP) { - ALOGI("Removing device %s due to epoll hang-up event.", - device->identifier.name.c_str()); - deviceChanged = true; - closeDeviceLocked(device); - } else { - ALOGW("Received unexpected epoll event 0x%08x for device %s.", - eventItem.events, device->identifier.name.c_str()); - } - } - - // readNotify() will modify the list of devices so this must be done after - // processing all other events to ensure that we read all remaining events - // before closing the devices. - if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { - mPendingINotify = false; - readNotifyLocked(); - deviceChanged = true; - } - - // Report added or removed devices immediately. - if (deviceChanged) { - continue; - } - - // Return now if we have collected any events or if we were explicitly awoken. - if (event != buffer || awoken) { - break; - } - - // Poll for events. Mind the wake lock dance! - // We hold a wake lock at all times except during epoll_wait(). This works due to some - // subtle choreography. When a device driver has pending (unread) events, it acquires - // a kernel wake lock. However, once the last pending event has been read, the device - // driver will release the kernel wake lock. To prevent the system from going to sleep - // when this happens, the EventHub holds onto its own user wake lock while the client - // is processing events. Thus the system can only sleep if there are no events - // pending or currently being processed. - // - // The timeout is advisory only. If the device is asleep, it will not wake just to - // service the timeout. - mPendingEventIndex = 0; - - mLock.unlock(); // release lock before poll, must be before release_wake_lock - release_wake_lock(WAKE_LOCK_ID); - - int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); - - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock - - if (pollResult == 0) { - // Timed out. - mPendingEventCount = 0; - break; - } - - if (pollResult < 0) { - // An error occurred. - mPendingEventCount = 0; - - // Sleep after errors to avoid locking up the system. - // Hopefully the error is transient. - if (errno != EINTR) { - ALOGW("poll failed (errno=%d)\n", errno); - usleep(100000); - } - } else { - // Some events occurred. - mPendingEventCount = size_t(pollResult); - } - } - - // All done, return the number of events we read. - return event - buffer; -} - -std::vector EventHub::getVideoFrames(int32_t deviceId) { - AutoMutex _l(mLock); - - Device* device = getDeviceLocked(deviceId); - if (!device || !device->videoDevice) { - return {}; - } - return device->videoDevice->consumeFrames(); -} - -void EventHub::wake() { - ALOGV("wake() called"); - - ssize_t nWrite; - do { - nWrite = write(mWakeWritePipeFd, "W", 1); - } while (nWrite == -1 && errno == EINTR); - - if (nWrite != 1 && errno != EAGAIN) { - ALOGW("Could not write wake signal: %s", strerror(errno)); - } -} - -void EventHub::scanDevicesLocked() { - status_t result = scanDirLocked(DEVICE_PATH); - if(result < 0) { - ALOGE("scan dir failed for %s", DEVICE_PATH); - } - if (isV4lScanningEnabled()) { - result = scanVideoDirLocked(VIDEO_DEVICE_PATH); - if (result != OK) { - ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH); - } - } - if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) { - createVirtualKeyboardLocked(); - } -} - -// ---------------------------------------------------------------------------- - -static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { - const uint8_t* end = array + endIndex; - array += startIndex; - while (array != end) { - if (*(array++) != 0) { - return true; - } - } - return false; -} - -static const int32_t GAMEPAD_KEYCODES[] = { - AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, - AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, - AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, - AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, - AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, -}; - -status_t EventHub::registerFdForEpoll(int fd) { - // TODO(b/121395353) - consider adding EPOLLRDHUP - struct epoll_event eventItem = {}; - eventItem.events = EPOLLIN | EPOLLWAKEUP; - eventItem.data.fd = fd; - if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { - ALOGE("Could not add fd to epoll instance: %s", strerror(errno)); - return -errno; - } - return OK; -} - -status_t EventHub::unregisterFdFromEpoll(int fd) { - if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr)) { - ALOGW("Could not remove fd from epoll instance: %s", strerror(errno)); - return -errno; - } - return OK; -} - -status_t EventHub::registerDeviceForEpollLocked(Device* device) { - if (device == nullptr) { - if (DEBUG) { - LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device"); - } - return BAD_VALUE; - } - status_t result = registerFdForEpoll(device->fd); - if (result != OK) { - ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id); - return result; - } - if (device->videoDevice) { - registerVideoDeviceForEpollLocked(*device->videoDevice); - } - return result; -} - -void EventHub::registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) { - status_t result = registerFdForEpoll(videoDevice.getFd()); - if (result != OK) { - ALOGE("Could not add video device %s to epoll", videoDevice.getName().c_str()); - } -} - -status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) { - if (device->hasValidFd()) { - status_t result = unregisterFdFromEpoll(device->fd); - if (result != OK) { - ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id); - return result; - } - } - if (device->videoDevice) { - unregisterVideoDeviceFromEpollLocked(*device->videoDevice); - } - return OK; -} - -void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) { - if (videoDevice.hasValidFd()) { - status_t result = unregisterFdFromEpoll(videoDevice.getFd()); - if (result != OK) { - ALOGW("Could not remove video device fd from epoll for device: %s", - videoDevice.getName().c_str()); - } - } -} - -status_t EventHub::openDeviceLocked(const char* devicePath) { - char buffer[80]; - - ALOGV("Opening device: %s", devicePath); - - int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK); - if(fd < 0) { - ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); - return -1; - } - - InputDeviceIdentifier identifier; - - // Get device name. - if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { - ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno)); - } else { - buffer[sizeof(buffer) - 1] = '\0'; - identifier.name = buffer; - } - - // Check to see if the device is on our excluded list - for (size_t i = 0; i < mExcludedDevices.size(); i++) { - const std::string& item = mExcludedDevices[i]; - if (identifier.name == item) { - ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str()); - close(fd); - return -1; - } - } - - // Get device driver version. - int driverVersion; - if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { - ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); - close(fd); - return -1; - } - - // Get device identifier. - struct input_id inputId; - if(ioctl(fd, EVIOCGID, &inputId)) { - ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); - close(fd); - return -1; - } - identifier.bus = inputId.bustype; - identifier.product = inputId.product; - identifier.vendor = inputId.vendor; - identifier.version = inputId.version; - - // Get device physical location. - if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); - } else { - buffer[sizeof(buffer) - 1] = '\0'; - identifier.location = buffer; - } - - // Get device unique id. - if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); - } else { - buffer[sizeof(buffer) - 1] = '\0'; - identifier.uniqueId = buffer; - } - - // Fill in the descriptor. - assignDescriptorLocked(identifier); - - // Allocate device. (The device object takes ownership of the fd at this point.) - int32_t deviceId = mNextDeviceId++; - Device* device = new Device(fd, deviceId, devicePath, identifier); - - ALOGV("add device %d: %s\n", deviceId, devicePath); - ALOGV(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", - identifier.bus, identifier.vendor, identifier.product, identifier.version); - ALOGV(" name: \"%s\"\n", identifier.name.c_str()); - ALOGV(" location: \"%s\"\n", identifier.location.c_str()); - ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str()); - ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str()); - ALOGV(" driver: v%d.%d.%d\n", - driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); - - // Load the configuration file for the device. - loadConfigurationLocked(device); - - // Figure out the kinds of events the device reports. - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); - ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); - ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); - ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask); - ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); - - // See if this is a keyboard. Ignore everything in the button range except for - // joystick and gamepad buttons which are handled like keyboards for the most part. - bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), - sizeof_bit_array(KEY_MAX + 1)); - bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), - sizeof_bit_array(BTN_MOUSE)) - || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), - sizeof_bit_array(BTN_DIGI)); - if (haveKeyboardKeys || haveGamepadButtons) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; - } - - // See if this is a cursor device such as a trackball or mouse. - if (test_bit(BTN_MOUSE, device->keyBitmask) - && test_bit(REL_X, device->relBitmask) - && test_bit(REL_Y, device->relBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_CURSOR; - } - - // See if this is a rotary encoder type device. - String8 deviceType = String8(); - if (device->configuration && - device->configuration->tryGetProperty(String8("device.type"), deviceType)) { - if (!deviceType.compare(String8("rotaryEncoder"))) { - device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; - } - } - - // See if this is a touch pad. - // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, device->absBitmask) - && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { - // Some joysticks such as the PS3 controller report axes that conflict - // with the ABS_MT range. Try to confirm that the device really is - // a touch screen. - if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { - device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; - } - // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, device->keyBitmask) - && test_bit(ABS_X, device->absBitmask) - && test_bit(ABS_Y, device->absBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TOUCH; - // Is this a BT stylus? - } else if ((test_bit(ABS_PRESSURE, device->absBitmask) || - test_bit(BTN_TOUCH, device->keyBitmask)) - && !test_bit(ABS_X, device->absBitmask) - && !test_bit(ABS_Y, device->absBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; - // Keyboard will try to claim some of the buttons but we really want to reserve those so we - // can fuse it with the touch screen data, so just take them back. Note this means an - // external stylus cannot also be a keyboard device. - device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD; - } - - // See if this device is a joystick. - // Assumes that joysticks always have gamepad buttons in order to distinguish them - // from other devices such as accelerometers that also have absolute axes. - if (haveGamepadButtons) { - uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK; - for (int i = 0; i <= ABS_MAX; i++) { - if (test_bit(i, device->absBitmask) - && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { - device->classes = assumedClasses; - break; - } - } - } - - // Check whether this device has switches. - for (int i = 0; i <= SW_MAX; i++) { - if (test_bit(i, device->swBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_SWITCH; - break; - } - } - - // Check whether this device supports the vibrator. - if (test_bit(FF_RUMBLE, device->ffBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_VIBRATOR; - } - - // Configure virtual keys. - if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { - // Load the virtual keys for the touch screen, if any. - // We do this now so that we can make sure to load the keymap if necessary. - bool success = loadVirtualKeyMapLocked(device); - if (success) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; - } - } - - // Load the key map. - // We need to do this for joysticks too because the key layout may specify axes. - status_t keyMapStatus = NAME_NOT_FOUND; - if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { - // Load the keymap for the device. - keyMapStatus = loadKeyMapLocked(device); - } - - // Configure the keyboard, gamepad or virtual keyboard. - if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { - // Register the keyboard as a built-in keyboard if it is eligible. - if (!keyMapStatus - && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD - && isEligibleBuiltInKeyboard(device->identifier, - device->configuration, &device->keyMap)) { - mBuiltInKeyboardId = device->id; - } - - // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (hasKeycodeLocked(device, AKEYCODE_Q)) { - device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; - } - - // See if this device has a DPAD. - if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && - hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && - hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { - device->classes |= INPUT_DEVICE_CLASS_DPAD; - } - - // See if this device has a gamepad. - for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) { - if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { - device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; - break; - } - } - } - - // If the device isn't recognized as something we handle, don't monitor it. - if (device->classes == 0) { - ALOGV("Dropping device: id=%d, path='%s', name='%s'", - deviceId, devicePath, device->identifier.name.c_str()); - delete device; - return -1; - } - - // Determine whether the device has a mic. - if (deviceHasMicLocked(device)) { - device->classes |= INPUT_DEVICE_CLASS_MIC; - } - - // Determine whether the device is external or internal. - if (isExternalDeviceLocked(device)) { - device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; - } - - if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) - && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) { - device->controllerNumber = getNextControllerNumberLocked(device); - setLedForControllerLocked(device); - } - - // Find a matching video device by comparing device names - // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll - for (std::unique_ptr& videoDevice : mUnattachedVideoDevices) { - if (device->identifier.name == videoDevice->getName()) { - device->videoDevice = std::move(videoDevice); - break; - } - } - mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(), - mUnattachedVideoDevices.end(), - [](const std::unique_ptr& videoDevice){ - return videoDevice == nullptr; }), mUnattachedVideoDevices.end()); - - if (registerDeviceForEpollLocked(device) != OK) { - delete device; - return -1; - } - - configureFd(device); - - ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " - "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", - deviceId, fd, devicePath, device->identifier.name.c_str(), - device->classes, - device->configurationFile.c_str(), - device->keyMap.keyLayoutFile.c_str(), - device->keyMap.keyCharacterMapFile.c_str(), - toString(mBuiltInKeyboardId == deviceId)); - - addDeviceLocked(device); - return OK; -} - -void EventHub::configureFd(Device* device) { - // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type - if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { - // Disable kernel key repeat since we handle it ourselves - unsigned int repeatRate[] = {0, 0}; - if (ioctl(device->fd, EVIOCSREP, repeatRate)) { - ALOGW("Unable to disable kernel key repeat for %s: %s", - device->path.c_str(), strerror(errno)); - } - } - - // Tell the kernel that we want to use the monotonic clock for reporting timestamps - // associated with input events. This is important because the input system - // uses the timestamps extensively and assumes they were recorded using the monotonic - // clock. - int clockId = CLOCK_MONOTONIC; - bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); -} - -void EventHub::openVideoDeviceLocked(const std::string& devicePath) { - std::unique_ptr videoDevice = TouchVideoDevice::create(devicePath); - if (!videoDevice) { - ALOGE("Could not create touch video device for %s. Ignoring", devicePath.c_str()); - return; - } - // Transfer ownership of this video device to a matching input device - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); - if (videoDevice->getName() == device->identifier.name) { - device->videoDevice = std::move(videoDevice); - if (device->enabled) { - registerVideoDeviceForEpollLocked(*device->videoDevice); - } - return; - } - } - - // Couldn't find a matching input device, so just add it to a temporary holding queue. - // A matching input device may appear later. - ALOGI("Adding video device %s to list of unattached video devices", - videoDevice->getName().c_str()); - mUnattachedVideoDevices.push_back(std::move(videoDevice)); -} - -bool EventHub::isDeviceEnabled(int32_t deviceId) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) { - ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); - return false; - } - return device->enabled; -} - -status_t EventHub::enableDevice(int32_t deviceId) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) { - ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (device->enabled) { - ALOGW("Duplicate call to %s, input device %" PRId32 " already enabled", __func__, deviceId); - return OK; - } - status_t result = device->enable(); - if (result != OK) { - ALOGE("Failed to enable device %" PRId32, deviceId); - return result; - } - - configureFd(device); - - return registerDeviceForEpollLocked(device); -} - -status_t EventHub::disableDevice(int32_t deviceId) { - AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device == nullptr) { - ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (!device->enabled) { - ALOGW("Duplicate call to %s, input device already disabled", __func__); - return OK; - } - unregisterDeviceFromEpollLocked(device); - return device->disable(); -} - -void EventHub::createVirtualKeyboardLocked() { - InputDeviceIdentifier identifier; - identifier.name = "Virtual"; - identifier.uniqueId = ""; - assignDescriptorLocked(identifier); - - Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "", - identifier); - device->classes = INPUT_DEVICE_CLASS_KEYBOARD - | INPUT_DEVICE_CLASS_ALPHAKEY - | INPUT_DEVICE_CLASS_DPAD - | INPUT_DEVICE_CLASS_VIRTUAL; - loadKeyMapLocked(device); - addDeviceLocked(device); -} - -void EventHub::addDeviceLocked(Device* device) { - mDevices.add(device->id, device); - device->next = mOpeningDevices; - mOpeningDevices = device; -} - -void EventHub::loadConfigurationLocked(Device* device) { - device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( - device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); - if (device->configurationFile.empty()) { - ALOGD("No input device configuration file found for device '%s'.", - device->identifier.name.c_str()); - } else { - status_t status = PropertyMap::load(String8(device->configurationFile.c_str()), - &device->configuration); - if (status) { - ALOGE("Error loading input device configuration file for device '%s'. " - "Using default configuration.", - device->identifier.name.c_str()); - } - } -} - -bool EventHub::loadVirtualKeyMapLocked(Device* device) { - // The virtual key map is supplied by the kernel as a system board property file. - std::string path; - path += "/sys/board_properties/virtualkeys."; - path += device->identifier.getCanonicalName(); - if (access(path.c_str(), R_OK)) { - return false; - } - device->virtualKeyMap = VirtualKeyMap::load(path); - return device->virtualKeyMap != nullptr; -} - -status_t EventHub::loadKeyMapLocked(Device* device) { - return device->keyMap.load(device->identifier, device->configuration); -} - -bool EventHub::isExternalDeviceLocked(Device* device) { - if (device->configuration) { - bool value; - if (device->configuration->tryGetProperty(String8("device.internal"), value)) { - return !value; - } - } - return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH; -} - -bool EventHub::deviceHasMicLocked(Device* device) { - if (device->configuration) { - bool value; - if (device->configuration->tryGetProperty(String8("audio.mic"), value)) { - return value; - } - } - return false; -} - -int32_t EventHub::getNextControllerNumberLocked(Device* device) { - if (mControllerNumbers.isFull()) { - ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s", - device->identifier.name.c_str()); - return 0; - } - // Since the controller number 0 is reserved for non-controllers, translate all numbers up by - // one - return static_cast(mControllerNumbers.markFirstUnmarkedBit() + 1); -} - -void EventHub::releaseControllerNumberLocked(Device* device) { - int32_t num = device->controllerNumber; - device->controllerNumber= 0; - if (num == 0) { - return; - } - mControllerNumbers.clearBit(static_cast(num - 1)); -} - -void EventHub::setLedForControllerLocked(Device* device) { - for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) { - setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1); - } -} - -bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { - if (!device->keyMap.haveKeyLayout()) { - return false; - } - - std::vector scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); - const size_t N = scanCodes.size(); - for (size_t i=0; i= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { - return true; - } - } - - return false; -} - -status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const { - if (!device->keyMap.haveKeyLayout()) { - return NAME_NOT_FOUND; - } - - int32_t scanCode; - if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { - *outScanCode = scanCode; - return NO_ERROR; - } - } - return NAME_NOT_FOUND; -} - -void EventHub::closeDeviceByPathLocked(const char *devicePath) { - Device* device = getDeviceByPathLocked(devicePath); - if (device) { - closeDeviceLocked(device); - return; - } - ALOGV("Remove device: %s not found, device may already have been removed.", devicePath); -} - -/** - * Find the video device by filename, and close it. - * The video device is closed by path during an inotify event, where we don't have the - * additional context about the video device fd, or the associated input device. - */ -void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) { - // A video device may be owned by an existing input device, or it may be stored in - // the mUnattachedVideoDevices queue. Check both locations. - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); - if (device->videoDevice && device->videoDevice->getPath() == devicePath) { - unregisterVideoDeviceFromEpollLocked(*device->videoDevice); - device->videoDevice = nullptr; - return; - } - } - mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(), - mUnattachedVideoDevices.end(), [&devicePath]( - const std::unique_ptr& videoDevice) { - return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end()); -} - -void EventHub::closeAllDevicesLocked() { - mUnattachedVideoDevices.clear(); - while (mDevices.size() > 0) { - closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); - } -} - -void EventHub::closeDeviceLocked(Device* device) { - ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", - device->path.c_str(), device->identifier.name.c_str(), device->id, - device->fd, device->classes); - - if (device->id == mBuiltInKeyboardId) { - ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.c_str(), mBuiltInKeyboardId); - mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; - } - - unregisterDeviceFromEpollLocked(device); - if (device->videoDevice) { - // This must be done after the video device is removed from epoll - mUnattachedVideoDevices.push_back(std::move(device->videoDevice)); - } - - releaseControllerNumberLocked(device); - - mDevices.removeItem(device->id); - device->close(); - - // Unlink for opening devices list if it is present. - Device* pred = nullptr; - bool found = false; - for (Device* entry = mOpeningDevices; entry != nullptr; ) { - if (entry == device) { - found = true; - break; - } - pred = entry; - entry = entry->next; - } - if (found) { - // Unlink the device from the opening devices list then delete it. - // We don't need to tell the client that the device was closed because - // it does not even know it was opened in the first place. - ALOGI("Device %s was immediately closed after opening.", device->path.c_str()); - if (pred) { - pred->next = device->next; - } else { - mOpeningDevices = device->next; - } - delete device; - } else { - // Link into closing devices list. - // The device will be deleted later after we have informed the client. - device->next = mClosingDevices; - mClosingDevices = device; - } -} - -status_t EventHub::readNotifyLocked() { - int res; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event *event; - - ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); - res = read(mINotifyFd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - ALOGW("could not get event, %s\n", strerror(errno)); - return -1; - } - - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - if(event->len) { - if (event->wd == mInputWd) { - std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name); - if(event->mask & IN_CREATE) { - openDeviceLocked(filename.c_str()); - } else { - ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); - closeDeviceByPathLocked(filename.c_str()); - } - } - else if (event->wd == mVideoWd) { - if (isV4lTouchNode(event->name)) { - std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name); - if (event->mask & IN_CREATE) { - openVideoDeviceLocked(filename); - } else { - ALOGI("Removing video device '%s' due to inotify event", filename.c_str()); - closeVideoDeviceByPathLocked(filename); - } - } - } - else { - LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd); - } - } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - return 0; -} - -status_t EventHub::scanDirLocked(const char *dirname) -{ - char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; - dir = opendir(dirname); - if(dir == nullptr) - return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); - openDeviceLocked(devname); - } - closedir(dir); - return 0; -} - -/** - * Look for all dirname/v4l-touch* devices, and open them. - */ -status_t EventHub::scanVideoDirLocked(const std::string& dirname) -{ - DIR* dir; - struct dirent* de; - dir = opendir(dirname.c_str()); - if(!dir) { - ALOGE("Could not open video directory %s", dirname.c_str()); - return BAD_VALUE; - } - - while((de = readdir(dir))) { - const char* name = de->d_name; - if (isV4lTouchNode(name)) { - ALOGI("Found touch video device %s", name); - openVideoDeviceLocked(dirname + "/" + name); - } - } - closedir(dir); - return OK; -} - -void EventHub::requestReopenDevices() { - ALOGV("requestReopenDevices() called"); - - AutoMutex _l(mLock); - mNeedToReopenDevices = true; -} - -void EventHub::dump(std::string& dump) { - dump += "Event Hub State:\n"; - - { // acquire lock - AutoMutex _l(mLock); - - dump += StringPrintf(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId); - - dump += INDENT "Devices:\n"; - - for (size_t i = 0; i < mDevices.size(); i++) { - const Device* device = mDevices.valueAt(i); - if (mBuiltInKeyboardId == device->id) { - dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", - device->id, device->identifier.name.c_str()); - } else { - dump += StringPrintf(INDENT2 "%d: %s\n", device->id, - device->identifier.name.c_str()); - } - dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes); - dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str()); - dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled)); - dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str()); - dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.c_str()); - dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber); - dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str()); - dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " - "product=0x%04x, version=0x%04x\n", - device->identifier.bus, device->identifier.vendor, - device->identifier.product, device->identifier.version); - dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n", - device->keyMap.keyLayoutFile.c_str()); - dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", - device->keyMap.keyCharacterMapFile.c_str()); - dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", - device->configurationFile.c_str()); - dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", - toString(device->overlayKeyMap != nullptr)); - dump += INDENT3 "VideoDevice: "; - if (device->videoDevice) { - dump += device->videoDevice->dump() + "\n"; - } else { - dump += "\n"; - } - } - - dump += INDENT "Unattached video devices:\n"; - for (const std::unique_ptr& videoDevice : mUnattachedVideoDevices) { - dump += INDENT2 + videoDevice->dump() + "\n"; - } - if (mUnattachedVideoDevices.empty()) { - dump += INDENT2 "\n"; - } - } // release lock -} - -void EventHub::monitor() { - // Acquire and release the lock to ensure that the event hub has not deadlocked. - mLock.lock(); - mLock.unlock(); -} - - -}; // namespace android diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h deleted file mode 100644 index 6c3a4a2aa3..0000000000 --- a/services/inputflinger/EventHub.h +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -#ifndef _RUNTIME_EVENT_HUB_H -#define _RUNTIME_EVENT_HUB_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "TouchVideoDevice.h" - -/* Convenience constants. */ - -#define BTN_FIRST 0x100 // first button code -#define BTN_LAST 0x15f // last button code - -namespace android { - -/* - * A raw event as retrieved from the EventHub. - */ -struct RawEvent { - nsecs_t when; - int32_t deviceId; - int32_t type; - int32_t code; - int32_t value; -}; - -/* Describes an absolute axis. */ -struct RawAbsoluteAxisInfo { - bool valid; // true if the information is valid, false otherwise - - int32_t minValue; // minimum value - int32_t maxValue; // maximum value - int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 - int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - int32_t resolution; // resolution in units per mm or radians per mm - - inline void clear() { - valid = false; - minValue = 0; - maxValue = 0; - flat = 0; - fuzz = 0; - resolution = 0; - } -}; - -/* - * Input device classes. - */ -enum { - /* The input device is a keyboard or has buttons. */ - INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, - - /* The input device is an alpha-numeric keyboard (not just a dial pad). */ - INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, - - /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ - INPUT_DEVICE_CLASS_TOUCH = 0x00000004, - - /* The input device is a cursor device such as a trackball or mouse. */ - INPUT_DEVICE_CLASS_CURSOR = 0x00000008, - - /* The input device is a multi-touch touchscreen. */ - INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, - - /* The input device is a directional pad (implies keyboard, has DPAD keys). */ - INPUT_DEVICE_CLASS_DPAD = 0x00000020, - - /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ - INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, - - /* The input device has switches. */ - INPUT_DEVICE_CLASS_SWITCH = 0x00000080, - - /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ - INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, - - /* The input device has a vibrator (supports FF_RUMBLE). */ - INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, - - /* The input device has a microphone. */ - INPUT_DEVICE_CLASS_MIC = 0x00000400, - - /* The input device is an external stylus (has data we want to fuse with touch data). */ - INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, - - /* The input device has a rotary encoder */ - INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000, - - /* The input device is virtual (not a real device, not part of UI configuration). */ - INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, - - /* The input device is external (not built-in). */ - INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, -}; - -/* - * Gets the class that owns an axis, in cases where multiple classes might claim - * the same axis for different purposes. - */ -extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); - -/* - * Grand Central Station for events. - * - * The event hub aggregates input events received across all known input - * devices on the system, including devices that may be emulated by the simulator - * environment. In addition, the event hub generates fake input events to indicate - * when devices are added or removed. - * - * The event hub provides a stream of input events (via the getEvent function). - * It also supports querying the current actual state of input devices such as identifying - * which keys are currently down. Finally, the event hub keeps track of the capabilities of - * individual input devices, such as their class and the set of key codes that they support. - */ -class EventHubInterface { -public: - EventHubInterface() { } - virtual ~EventHubInterface() { } - - // Synthetic raw event type codes produced when devices are added or removed. - enum { - // Sent when a device is added. - DEVICE_ADDED = 0x10000000, - // Sent when a device is removed. - DEVICE_REMOVED = 0x20000000, - // Sent when all added/removed devices from the most recent scan have been reported. - // This event is always sent at least once. - FINISHED_DEVICE_SCAN = 0x30000000, - - FIRST_SYNTHETIC_EVENT = DEVICE_ADDED, - }; - - virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; - - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0; - - virtual int32_t getDeviceControllerNumber(int32_t deviceId) const = 0; - - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; - - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const = 0; - - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; - - virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; - - virtual status_t mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const = 0; - - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, - AxisInfo* outAxisInfo) const = 0; - - // Sets devices that are excluded from opening. - // This can be used to ignore input devices for sensors. - virtual void setExcludedDevices(const std::vector& devices) = 0; - - /* - * Wait for events to become available and returns them. - * After returning, the EventHub holds onto a wake lock until the next call to getEvent. - * This ensures that the device will not go to sleep while the event is being processed. - * If the device needs to remain awake longer than that, then the caller is responsible - * for taking care of it (say, by poking the power manager user activity timer). - * - * The timeout is advisory only. If the device is asleep, it will not wake just to - * service the timeout. - * - * Returns the number of events obtained, or 0 if the timeout expired. - */ - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0; - virtual std::vector getVideoFrames(int32_t deviceId) = 0; - - /* - * Query current input state. - */ - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const = 0; - - /* - * Examine key input devices for specific framework keycode support - */ - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const = 0; - - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; - - /* LED related functions expect Android LED constants, not scan codes or HID usages */ - virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; - virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; - - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector& outVirtualKeys) const = 0; - - virtual sp getKeyCharacterMap(int32_t deviceId) const = 0; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map) = 0; - - /* Control the vibrator. */ - virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0; - virtual void cancelVibrate(int32_t deviceId) = 0; - - /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ - virtual void requestReopenDevices() = 0; - - /* Wakes up getEvents() if it is blocked on a read. */ - virtual void wake() = 0; - - /* Dump EventHub state to a string. */ - virtual void dump(std::string& dump) = 0; - - /* Called by the heatbeat to ensures that the reader has not deadlocked. */ - virtual void monitor() = 0; - - /* Return true if the device is enabled. */ - virtual bool isDeviceEnabled(int32_t deviceId) = 0; - - /* Enable an input device */ - virtual status_t enableDevice(int32_t deviceId) = 0; - - /* Disable an input device. Closes file descriptor to that device. */ - virtual status_t disableDevice(int32_t deviceId) = 0; -}; - -class EventHub : public EventHubInterface -{ -public: - EventHub(); - - virtual uint32_t getDeviceClasses(int32_t deviceId) const; - - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const; - - virtual int32_t getDeviceControllerNumber(int32_t deviceId) const; - - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; - - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const; - - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; - - virtual bool hasInputProperty(int32_t deviceId, int property) const; - - virtual status_t mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const; - - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, - AxisInfo* outAxisInfo) const; - - virtual void setExcludedDevices(const std::vector& devices); - - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; - - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const; - - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); - virtual std::vector getVideoFrames(int32_t deviceId); - - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const; - virtual bool hasLed(int32_t deviceId, int32_t led) const; - virtual void setLedState(int32_t deviceId, int32_t led, bool on); - - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector& outVirtualKeys) const; - - virtual sp getKeyCharacterMap(int32_t deviceId) const; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map); - - virtual void vibrate(int32_t deviceId, nsecs_t duration); - virtual void cancelVibrate(int32_t deviceId); - - virtual void requestReopenDevices(); - - virtual void wake(); - - virtual void dump(std::string& dump); - virtual void monitor(); - - virtual ~EventHub(); - -private: - struct Device { - Device* next; - - int fd; // may be -1 if device is closed - const int32_t id; - const std::string path; - const InputDeviceIdentifier identifier; - - std::unique_ptr videoDevice; - - uint32_t classes; - - uint8_t keyBitmask[(KEY_MAX + 1) / 8]; - uint8_t absBitmask[(ABS_MAX + 1) / 8]; - uint8_t relBitmask[(REL_MAX + 1) / 8]; - uint8_t swBitmask[(SW_MAX + 1) / 8]; - uint8_t ledBitmask[(LED_MAX + 1) / 8]; - uint8_t ffBitmask[(FF_MAX + 1) / 8]; - uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; - - std::string configurationFile; - PropertyMap* configuration; - std::unique_ptr virtualKeyMap; - KeyMap keyMap; - - sp overlayKeyMap; - sp combinedKeyMap; - - bool ffEffectPlaying; - int16_t ffEffectId; // initially -1 - - int32_t controllerNumber; - - Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier); - ~Device(); - - void close(); - - bool enabled; // initially true - status_t enable(); - status_t disable(); - bool hasValidFd(); - const bool isVirtual; // set if fd < 0 is passed to constructor - - const sp& getKeyCharacterMap() const { - if (combinedKeyMap != nullptr) { - return combinedKeyMap; - } - return keyMap.keyCharacterMap; - } - }; - - status_t openDeviceLocked(const char* devicePath); - void openVideoDeviceLocked(const std::string& devicePath); - void createVirtualKeyboardLocked(); - void addDeviceLocked(Device* device); - void assignDescriptorLocked(InputDeviceIdentifier& identifier); - - void closeDeviceByPathLocked(const char *devicePath); - void closeVideoDeviceByPathLocked(const std::string& devicePath); - void closeDeviceLocked(Device* device); - void closeAllDevicesLocked(); - - void configureFd(Device* device); - - bool isDeviceEnabled(int32_t deviceId); - status_t enableDevice(int32_t deviceId); - status_t disableDevice(int32_t deviceId); - status_t registerFdForEpoll(int fd); - status_t unregisterFdFromEpoll(int fd); - status_t registerDeviceForEpollLocked(Device* device); - void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice); - status_t unregisterDeviceFromEpollLocked(Device* device); - void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice); - - status_t scanDirLocked(const char *dirname); - status_t scanVideoDirLocked(const std::string& dirname); - void scanDevicesLocked(); - status_t readNotifyLocked(); - - Device* getDeviceByDescriptorLocked(const std::string& descriptor) const; - Device* getDeviceLocked(int32_t deviceId) const; - Device* getDeviceByPathLocked(const char* devicePath) const; - /** - * Look through all available fd's (both for input devices and for video devices), - * and return the device pointer. - */ - Device* getDeviceByFdLocked(int fd) const; - - bool hasKeycodeLocked(Device* device, int keycode) const; - - void loadConfigurationLocked(Device* device); - bool loadVirtualKeyMapLocked(Device* device); - status_t loadKeyMapLocked(Device* device); - - bool isExternalDeviceLocked(Device* device); - bool deviceHasMicLocked(Device* device); - - int32_t getNextControllerNumberLocked(Device* device); - void releaseControllerNumberLocked(Device* device); - void setLedForControllerLocked(Device* device); - - status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const; - void setLedStateLocked(Device* device, int32_t led, bool on); - - // Protect all internal state. - mutable Mutex mLock; - - // The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none. - // EventHub remaps the built-in keyboard to id 0 externally as required by the API. - enum { - // Must not conflict with any other assigned device ids, including - // the virtual keyboard id (-1). - NO_BUILT_IN_KEYBOARD = -2, - }; - int32_t mBuiltInKeyboardId; - - int32_t mNextDeviceId; - - BitSet32 mControllerNumbers; - - KeyedVector mDevices; - /** - * Video devices that report touchscreen heatmap, but have not (yet) been paired - * with a specific input device. Video device discovery is independent from input device - * discovery, so the two types of devices could be found in any order. - * Ideally, video devices in this queue do not have an open fd, or at least aren't - * actively streaming. - */ - std::vector> mUnattachedVideoDevices; - - Device *mOpeningDevices; - Device *mClosingDevices; - - bool mNeedToSendFinishedDeviceScan; - bool mNeedToReopenDevices; - bool mNeedToScanDevices; - std::vector mExcludedDevices; - - int mEpollFd; - int mINotifyFd; - int mWakeReadPipeFd; - int mWakeWritePipeFd; - - int mInputWd; - int mVideoWd; - - // Maximum number of signalled FDs to handle at a time. - static const int EPOLL_MAX_EVENTS = 16; - - // The array of pending epoll events and the index of the next event to be handled. - struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; - size_t mPendingEventCount; - size_t mPendingEventIndex; - bool mPendingINotify; -}; - -}; // namespace android - -#endif // _RUNTIME_EVENT_HUB_H diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index ba0b28786b..f3da324ba4 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -21,9 +21,7 @@ * Native input manager. */ -#include "EventHub.h" #include "InputClassifier.h" -#include "InputReader.h" #include "InputReaderBase.h" #include diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp deleted file mode 100644 index d59f274ab0..0000000000 --- a/services/inputflinger/InputReader.cpp +++ /dev/null @@ -1,7575 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputReader" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -// Log debug messages about gesture detection. -#define DEBUG_GESTURES 0 - -// Log debug messages about the vibrator. -#define DEBUG_VIBRATOR 0 - -// Log debug messages about fusing stylus data. -#define DEBUG_STYLUS_FUSION 0 - -#include "InputReader.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " -#define INDENT5 " " - -using android::base::StringPrintf; - -namespace android { - -// --- Constants --- - -// Maximum number of slots supported when using the slot-based Multitouch Protocol B. -static constexpr size_t MAX_SLOTS = 32; - -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - -// Artificial latency on synthetic events created from stylus data without corresponding touch -// data. -static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); - -// --- Static Functions --- - -template -inline static T abs(const T& value) { - return value < 0 ? - value : value; -} - -template -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; -} - -inline static float avg(float x, float y) { - return (x + y) / 2; -} - -inline static float distance(float x1, float y1, float x2, float y2) { - return hypotf(x1 - x2, y1 - y2); -} - -inline static int32_t signExtendNybble(int32_t value) { - return value >= 8 ? value - 16 : value; -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { - if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; - } - } - } - return value; -} - -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT }, - { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN }, - { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT }, - { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP }, - { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT }, - { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN }, - { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT }, - { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP }, -}; -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, - const int32_t map[][2], size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; -} - -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - { AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY }, - { AKEYCODE_STEM_1, AKEYCODE_STEM_1 }, - { AKEYCODE_STEM_2, AKEYCODE_STEM_2 }, - { AKEYCODE_STEM_3, AKEYCODE_STEM_3 }, -}; -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, - stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, - keyCodeRotationMap, keyCodeRotationMapSize); -} - -static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { - float temp; - switch (orientation) { - case DISPLAY_ORIENTATION_90: - temp = *deltaX; - *deltaX = *deltaY; - *deltaY = -temp; - break; - - case DISPLAY_ORIENTATION_180: - *deltaX = -*deltaX; - *deltaY = -*deltaY; - break; - - case DISPLAY_ORIENTATION_270: - temp = *deltaX; - *deltaX = -*deltaY; - *deltaY = temp; - break; - } -} - -static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { - return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; -} - -// Returns true if the pointer should be reported as being down given the specified -// button states. This determines whether the event is reported as a touch event. -static bool isPointerDown(int32_t buttonState) { - return buttonState & - (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY - | AMOTION_EVENT_BUTTON_TERTIARY); -} - -static float calculateCommonVector(float a, float b) { - if (a > 0 && b > 0) { - return a < b ? a : b; - } else if (a < 0 && b < 0) { - return a > b ? a : b; - } else { - return 0; - } -} - -static void synthesizeButtonKey(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState, - int32_t buttonState, int32_t keyCode) { - if ( - (action == AKEY_EVENT_ACTION_DOWN - && !(lastButtonState & buttonState) - && (currentButtonState & buttonState)) - || (action == AKEY_EVENT_ACTION_UP - && (lastButtonState & buttonState) - && !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); - } -} - -static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, - AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, - AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD); -} - - -// --- InputReader --- - -InputReader::InputReader(std::shared_ptr eventHub, - const sp& policy, - const sp& listener) - : mContext(this), - mEventHub(eventHub), - mPolicy(policy), - mNextSequenceNum(1), - mGlobalMetaState(0), - mGeneration(1), - mDisableVirtualKeysTimeout(LLONG_MIN), - mNextTimeout(LLONG_MAX), - mConfigurationChangesToRefresh(0) { - mQueuedListener = new QueuedInputListener(listener); - - { // acquire lock - AutoMutex _l(mLock); - - refreshConfigurationLocked(0); - updateGlobalMetaStateLocked(); - } // release lock -} - -InputReader::~InputReader() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } -} - -void InputReader::loopOnce() { - int32_t oldGeneration; - int32_t timeoutMillis; - bool inputDevicesChanged = false; - std::vector inputDevices; - { // acquire lock - AutoMutex _l(mLock); - - oldGeneration = mGeneration; - timeoutMillis = -1; - - uint32_t changes = mConfigurationChangesToRefresh; - if (changes) { - mConfigurationChangesToRefresh = 0; - timeoutMillis = 0; - refreshConfigurationLocked(changes); - } else if (mNextTimeout != LLONG_MAX) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); - } - } // release lock - - size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); - - { // acquire lock - AutoMutex _l(mLock); - mReaderIsAliveCondition.broadcast(); - - if (count) { - processEventsLocked(mEventBuffer, count); - } - - if (mNextTimeout != LLONG_MAX) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now >= mNextTimeout) { -#if DEBUG_RAW_EVENTS - ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); -#endif - mNextTimeout = LLONG_MAX; - timeoutExpiredLocked(now); - } - } - - if (oldGeneration != mGeneration) { - inputDevicesChanged = true; - getInputDevicesLocked(inputDevices); - } - } // release lock - - // Send out a message that the describes the changed input devices. - if (inputDevicesChanged) { - mPolicy->notifyInputDevicesChanged(inputDevices); - } - - // Flush queued events out to the listener. - // This must happen outside of the lock because the listener could potentially call - // back into the InputReader's methods, such as getScanCodeState, or become blocked - // on another thread similarly waiting to acquire the InputReader lock thereby - // resulting in a deadlock. This situation is actually quite plausible because the - // listener is actually the input dispatcher, which calls into the window manager, - // which occasionally calls into the input reader. - mQueuedListener->flush(); -} - -void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { - for (const RawEvent* rawEvent = rawEvents; count;) { - int32_t type = rawEvent->type; - size_t batchSize = 1; - if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { - int32_t deviceId = rawEvent->deviceId; - while (batchSize < count) { - if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT - || rawEvent[batchSize].deviceId != deviceId) { - break; - } - batchSize += 1; - } -#if DEBUG_RAW_EVENTS - ALOGD("BatchSize: %zu Count: %zu", batchSize, count); -#endif - processEventsForDeviceLocked(deviceId, rawEvent, batchSize); - } else { - switch (rawEvent->type) { - case EventHubInterface::DEVICE_ADDED: - addDeviceLocked(rawEvent->when, rawEvent->deviceId); - break; - case EventHubInterface::DEVICE_REMOVED: - removeDeviceLocked(rawEvent->when, rawEvent->deviceId); - break; - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChangedLocked(rawEvent->when); - break; - default: - ALOG_ASSERT(false); // can't happen - break; - } - } - count -= batchSize; - rawEvent += batchSize; - } -} - -void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); - return; - } - - InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); - - InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); - device->configure(when, &mConfig, 0); - device->reset(when); - - if (device->isIgnored()) { - ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, - identifier.name.c_str()); - } else { - ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, - identifier.name.c_str(), device->getSources()); - } - - mDevices.add(deviceId, device); - bumpGenerationLocked(); - - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - notifyExternalStylusPresenceChanged(); - } -} - -void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - InputDevice* device = nullptr; - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); - return; - } - - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); - bumpGenerationLocked(); - - if (device->isIgnored()) { - ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", - device->getId(), device->getName().c_str()); - } else { - ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", - device->getId(), device->getName().c_str(), device->getSources()); - } - - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - notifyExternalStylusPresenceChanged(); - } - - device->reset(when); - delete device; -} - -InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, uint32_t classes) { - InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), - controllerNumber, identifier, classes); - - // External devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { - device->setExternal(true); - } - - // Devices with mics. - if (classes & INPUT_DEVICE_CLASS_MIC) { - device->setMic(true); - } - - // Switch-like devices. - if (classes & INPUT_DEVICE_CLASS_SWITCH) { - device->addMapper(new SwitchInputMapper(device)); - } - - // Scroll wheel-like devices. - if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { - device->addMapper(new RotaryEncoderInputMapper(device)); - } - - // Vibrator-like devices. - if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { - device->addMapper(new VibratorInputMapper(device)); - } - - // Keyboard-like devices. - uint32_t keyboardSource = 0; - int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; - if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { - keyboardSource |= AINPUT_SOURCE_KEYBOARD; - } - if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; - } - if (classes & INPUT_DEVICE_CLASS_DPAD) { - keyboardSource |= AINPUT_SOURCE_DPAD; - } - if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { - keyboardSource |= AINPUT_SOURCE_GAMEPAD; - } - - if (keyboardSource != 0) { - device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); - } - - // Cursor-like devices. - if (classes & INPUT_DEVICE_CLASS_CURSOR) { - device->addMapper(new CursorInputMapper(device)); - } - - // Touchscreens and touchpad devices. - if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { - device->addMapper(new MultiTouchInputMapper(device)); - } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { - device->addMapper(new SingleTouchInputMapper(device)); - } - - // Joystick-like devices. - if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { - device->addMapper(new JoystickInputMapper(device)); - } - - // External stylus-like devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - device->addMapper(new ExternalStylusInputMapper(device)); - } - - return device; -} - -void InputReader::processEventsForDeviceLocked(int32_t deviceId, - const RawEvent* rawEvents, size_t count) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Discarding event for unknown deviceId %d.", deviceId); - return; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - //ALOGD("Discarding event for ignored deviceId %d.", deviceId); - return; - } - - device->process(rawEvents, count); -} - -void InputReader::timeoutExpiredLocked(nsecs_t when) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (!device->isIgnored()) { - device->timeoutExpired(when); - } - } -} - -void InputReader::handleConfigurationChangedLocked(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - updateGlobalMetaStateLocked(); - - // Enqueue configuration changed. - NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); - mQueuedListener->notifyConfigurationChanged(&args); -} - -void InputReader::refreshConfigurationLocked(uint32_t changes) { - mPolicy->getReaderConfiguration(&mConfig); - mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); - - if (changes) { - ALOGI("Reconfiguring input devices, changes=%s", - InputReaderConfiguration::changesToString(changes).c_str()); - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { - mEventHub->requestReopenDevices(); - } else { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->configure(now, &mConfig, changes); - } - } - } -} - -void InputReader::updateGlobalMetaStateLocked() { - mGlobalMetaState = 0; - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - mGlobalMetaState |= device->getMetaState(); - } -} - -int32_t InputReader::getGlobalMetaStateLocked() { - return mGlobalMetaState; -} - -void InputReader::notifyExternalStylusPresenceChanged() { - refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); -} - -void InputReader::getExternalStylusDevicesLocked(std::vector& outDevices) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { - InputDeviceInfo info; - device->getDeviceInfo(&info); - outDevices.push_back(info); - } - } -} - -void InputReader::dispatchExternalStylusState(const StylusState& state) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->updateExternalStylusState(state); - } -} - -void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { - mDisableVirtualKeysTimeout = time; -} - -bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - if (now < mDisableVirtualKeysTimeout) { - ALOGI("Dropping virtual key from device %s because virtual keys are " - "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().c_str(), - (mDisableVirtualKeysTimeout - now) * 0.000001, - keyCode, scanCode); - return true; - } else { - return false; - } -} - -void InputReader::fadePointerLocked() { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->fadePointer(); - } -} - -void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { - if (when < mNextTimeout) { - mNextTimeout = when; - mEventHub->wake(); - } -} - -int32_t InputReader::bumpGenerationLocked() { - return ++mGeneration; -} - -void InputReader::getInputDevices(std::vector& outInputDevices) { - AutoMutex _l(mLock); - getInputDevicesLocked(outInputDevices); -} - -void InputReader::getInputDevicesLocked(std::vector& outInputDevices) { - outInputDevices.clear(); - - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (!device->isIgnored()) { - InputDeviceInfo info; - device->getDeviceInfo(&info); - outInputDevices.push_back(info); - } - } -} - -int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState); -} - -int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState); -} - -int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState); -} - -int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, - GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. - int32_t currentResult = (device->*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; - } - } - } - } - return result; -} - -void InputReader::toggleCapsLockState(int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); - return; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - return; - } - - device->updateMetaState(AKEYCODE_CAPS_LOCK); -} - -bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - AutoMutex _l(mLock); - - memset(outFlags, 0, numCodes); - return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags); -} - -bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result |= device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } - return result; -} - -void InputReader::requestRefreshConfiguration(uint32_t changes) { - AutoMutex _l(mLock); - - if (changes) { - bool needWake = !mConfigurationChangesToRefresh; - mConfigurationChangesToRefresh |= changes; - - if (needWake) { - mEventHub->wake(); - } - } -} - -void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - device->vibrate(pattern, patternSize, repeat, token); - } -} - -void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - device->cancelVibrate(token); - } -} - -bool InputReader::isInputDeviceEnabled(int32_t deviceId) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - return device->isEnabled(); - } - ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); - return false; -} - -bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); - return false; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (!device->isEnabled()) { - ALOGW("Ignoring disabled device %s", device->getName().c_str()); - return false; - } - - std::optional associatedDisplayId = device->getAssociatedDisplayId(); - // No associated display. By default, can dispatch to all displays. - if (!associatedDisplayId) { - return true; - } - - if (*associatedDisplayId == ADISPLAY_ID_NONE) { - ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str()); - return true; - } - - return *associatedDisplayId == displayId; -} - -void InputReader::dump(std::string& dump) { - AutoMutex _l(mLock); - - mEventHub->dump(dump); - dump += "\n"; - - dump += "Input Reader State:\n"; - - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); - } - - dump += INDENT "Configuration:\n"; - dump += INDENT2 "ExcludedDeviceNames: ["; - for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) { - if (i != 0) { - dump += ", "; - } - dump += mConfig.excludedDeviceNames[i]; - } - dump += "]\n"; - dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", - mConfig.virtualKeyQuietTime * 0.000001f); - - dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: " - "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n", - mConfig.pointerVelocityControlParameters.scale, - mConfig.pointerVelocityControlParameters.lowThreshold, - mConfig.pointerVelocityControlParameters.highThreshold, - mConfig.pointerVelocityControlParameters.acceleration); - - dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: " - "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n", - mConfig.wheelVelocityControlParameters.scale, - mConfig.wheelVelocityControlParameters.lowThreshold, - mConfig.wheelVelocityControlParameters.highThreshold, - mConfig.wheelVelocityControlParameters.acceleration); - - dump += StringPrintf(INDENT2 "PointerGesture:\n"); - dump += StringPrintf(INDENT3 "Enabled: %s\n", - toString(mConfig.pointerGesturesEnabled)); - dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n", - mConfig.pointerGestureQuietInterval * 0.000001f); - dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n", - mConfig.pointerGestureDragMinSwitchSpeed); - dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n", - mConfig.pointerGestureTapInterval * 0.000001f); - dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n", - mConfig.pointerGestureTapDragInterval * 0.000001f); - dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", - mConfig.pointerGestureTapSlop); - dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n", - mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); - dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n", - mConfig.pointerGestureMultitouchMinDistance); - dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", - mConfig.pointerGestureSwipeTransitionAngleCosine); - dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", - mConfig.pointerGestureSwipeMaxWidthRatio); - dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n", - mConfig.pointerGestureMovementSpeedRatio); - dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", - mConfig.pointerGestureZoomSpeedRatio); - - dump += INDENT3 "Viewports:\n"; - mConfig.dump(dump); -} - -void InputReader::monitor() { - // Acquire and release the lock to ensure that the reader has not deadlocked. - mLock.lock(); - mEventHub->wake(); - mReaderIsAliveCondition.wait(mLock); - mLock.unlock(); - - // Check the EventHub - mEventHub->monitor(); -} - - -// --- InputReader::ContextImpl --- - -InputReader::ContextImpl::ContextImpl(InputReader* reader) : - mReader(reader) { -} - -void InputReader::ContextImpl::updateGlobalMetaState() { - // lock is already held by the input loop - mReader->updateGlobalMetaStateLocked(); -} - -int32_t InputReader::ContextImpl::getGlobalMetaState() { - // lock is already held by the input loop - return mReader->getGlobalMetaStateLocked(); -} - -void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { - // lock is already held by the input loop - mReader->disableVirtualKeysUntilLocked(time); -} - -bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - // lock is already held by the input loop - return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode); -} - -void InputReader::ContextImpl::fadePointer() { - // lock is already held by the input loop - mReader->fadePointerLocked(); -} - -void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { - // lock is already held by the input loop - mReader->requestTimeoutAtTimeLocked(when); -} - -int32_t InputReader::ContextImpl::bumpGeneration() { - // lock is already held by the input loop - return mReader->bumpGenerationLocked(); -} - -void InputReader::ContextImpl::getExternalStylusDevices(std::vector& outDevices) { - // lock is already held by whatever called refreshConfigurationLocked - mReader->getExternalStylusDevicesLocked(outDevices); -} - -void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { - mReader->dispatchExternalStylusState(state); -} - -InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { - return mReader->mPolicy.get(); -} - -InputListenerInterface* InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener.get(); -} - -EventHubInterface* InputReader::ContextImpl::getEventHub() { - return mReader->mEventHub.get(); -} - -uint32_t InputReader::ContextImpl::getNextSequenceNum() { - return (mReader->mNextSequenceNum)++; -} - -// --- InputDevice --- - -InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) : - mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber), - mIdentifier(identifier), mClasses(classes), - mSources(0), mIsExternal(false), mHasMic(false), mDropUntilNextSync(false) { -} - -InputDevice::~InputDevice() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - delete mMappers[i]; - } - mMappers.clear(); -} - -bool InputDevice::isEnabled() { - return getEventHub()->isDeviceEnabled(mId); -} - -void InputDevice::setEnabled(bool enabled, nsecs_t when) { - if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { - ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " - "but the corresponding viewport is not found", - getName().c_str(), *mAssociatedDisplayPort); - enabled = false; - } - - if (isEnabled() == enabled) { - return; - } - - if (enabled) { - getEventHub()->enableDevice(mId); - reset(when); - } else { - reset(when); - getEventHub()->disableDevice(mId); - } - // Must change generation to flag this device as changed - bumpGeneration(); -} - -void InputDevice::dump(std::string& dump) { - InputDeviceInfo deviceInfo; - getDeviceInfo(&deviceInfo); - - dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), - deviceInfo.getDisplayName().c_str()); - dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); - dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); - dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); - if (mAssociatedDisplayPort) { - dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); - } else { - dump += "\n"; - } - dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); - dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); - dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - - const std::vector& ranges = deviceInfo.getMotionRanges(); - if (!ranges.empty()) { - dump += INDENT2 "Motion Ranges:\n"; - for (size_t i = 0; i < ranges.size(); i++) { - const InputDeviceInfo::MotionRange& range = ranges[i]; - const char* label = getAxisLabel(range.axis); - char name[32]; - if (label) { - strncpy(name, label, sizeof(name)); - name[sizeof(name) - 1] = '\0'; - } else { - snprintf(name, sizeof(name), "%d", range.axis); - } - dump += StringPrintf(INDENT3 "%s: source=0x%08x, " - "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", - name, range.source, range.min, range.max, range.flat, range.fuzz, - range.resolution); - } - } - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } -} - -void InputDevice::addMapper(InputMapper* mapper) { - mMappers.push_back(mapper); -} - -void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { - mSources = 0; - - if (!isIgnored()) { - if (!changes) { // first time only - mContext->getEventHub()->getConfiguration(mId, &mConfiguration); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - sp keyboardLayout = - mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); - if (mAlias != alias) { - mAlias = alias; - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - auto it = config->disabledDevices.find(mId); - bool enabled = it == config->disabledDevices.end(); - setEnabled(enabled, when); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - // In most situations, no port will be specified. - mAssociatedDisplayPort = std::nullopt; - mAssociatedViewport = std::nullopt; - // Find the display port that corresponds to the current input port. - const std::string& inputPort = mIdentifier.location; - if (!inputPort.empty()) { - const std::unordered_map& ports = config->portAssociations; - const auto& displayPort = ports.find(inputPort); - if (displayPort != ports.end()) { - mAssociatedDisplayPort = std::make_optional(displayPort->second); - } - } - - // If the device was explicitly disabled by the user, it would be present in the - // "disabledDevices" list. If it is associated with a specific display, and it was not - // explicitly disabled, then enable/disable the device based on whether we can find the - // corresponding viewport. - bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); - if (mAssociatedDisplayPort) { - mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); - if (!mAssociatedViewport) { - ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " - "but the corresponding viewport is not found.", - getName().c_str(), *mAssociatedDisplayPort); - enabled = false; - } - } - - if (changes) { - // For first-time configuration, only allow device to be disabled after mappers have - // finished configuring. This is because we need to read some of the properties from - // the device's open fd. - setEnabled(enabled, when); - } - } - - for (InputMapper* mapper : mMappers) { - mapper->configure(when, config, changes); - mSources |= mapper->getSources(); - } - - // If a device is just plugged but it might be disabled, we need to update some info like - // axis range of touch from each InputMapper first, then disable it. - if (!changes) { - setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); - } - } -} - -void InputDevice::reset(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->reset(when); - } - - mContext->updateGlobalMetaState(); - - notifyReset(when); -} - -void InputDevice::process(const RawEvent* rawEvents, size_t count) { - // Process all of the events in order for each mapper. - // We cannot simply ask each mapper to process them in bulk because mappers may - // have side-effects that must be interleaved. For example, joystick movement events and - // gamepad button presses are handled by different mappers but they should be dispatched - // in the order received. - for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { -#if DEBUG_RAW_EVENTS - ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, - rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, - rawEvent->when); -#endif - - if (mDropUntilNextSync) { - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - mDropUntilNextSync = false; -#if DEBUG_RAW_EVENTS - ALOGD("Recovered from input event buffer overrun."); -#endif - } else { -#if DEBUG_RAW_EVENTS - ALOGD("Dropped input event while waiting for next input sync."); -#endif - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { - ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); - mDropUntilNextSync = true; - reset(rawEvent->when); - } else { - for (InputMapper* mapper : mMappers) { - mapper->process(rawEvent); - } - } - --count; - } -} - -void InputDevice::timeoutExpired(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->timeoutExpired(when); - } -} - -void InputDevice::updateExternalStylusState(const StylusState& state) { - for (InputMapper* mapper : mMappers) { - mapper->updateExternalStylusState(state); - } -} - -void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, - mIsExternal, mHasMic); - for (InputMapper* mapper : mMappers) { - mapper->populateDeviceInfo(outDeviceInfo); - } -} - -int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); -} - -int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); -} - -int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getState(sourceMask, switchCode, & InputMapper::getSwitchState); -} - -int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; - } - } - } - return result; -} - -bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } - } - return result; -} - -void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->vibrate(pattern, patternSize, repeat, token); - } -} - -void InputDevice::cancelVibrate(int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->cancelVibrate(token); - } -} - -void InputDevice::cancelTouch(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->cancelTouch(when); - } -} - -int32_t InputDevice::getMetaState() { - int32_t result = 0; - for (InputMapper* mapper : mMappers) { - result |= mapper->getMetaState(); - } - return result; -} - -void InputDevice::updateMetaState(int32_t keyCode) { - for (InputMapper* mapper : mMappers) { - mapper->updateMetaState(keyCode); - } -} - -void InputDevice::fadePointer() { - for (InputMapper* mapper : mMappers) { - mapper->fadePointer(); - } -} - -void InputDevice::bumpGeneration() { - mGeneration = mContext->bumpGeneration(); -} - -void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); -} - -std::optional InputDevice::getAssociatedDisplayId() { - // Check if we had associated to the specific display. - if (mAssociatedViewport) { - return mAssociatedViewport->displayId; - } - - // No associated display port, check if some InputMapper is associated. - for (InputMapper* mapper : mMappers) { - std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); - if (associatedDisplayId) { - return associatedDisplayId; - } - } - - return std::nullopt; -} - -// --- CursorButtonAccumulator --- - -CursorButtonAccumulator::CursorButtonAccumulator() { - clearButtons(); -} - -void CursorButtonAccumulator::reset(InputDevice* device) { - mBtnLeft = device->isKeyPressed(BTN_LEFT); - mBtnRight = device->isKeyPressed(BTN_RIGHT); - mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); - mBtnBack = device->isKeyPressed(BTN_BACK); - mBtnSide = device->isKeyPressed(BTN_SIDE); - mBtnForward = device->isKeyPressed(BTN_FORWARD); - mBtnExtra = device->isKeyPressed(BTN_EXTRA); - mBtnTask = device->isKeyPressed(BTN_TASK); -} - -void CursorButtonAccumulator::clearButtons() { - mBtnLeft = 0; - mBtnRight = 0; - mBtnMiddle = 0; - mBtnBack = 0; - mBtnSide = 0; - mBtnForward = 0; - mBtnExtra = 0; - mBtnTask = 0; -} - -void CursorButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_LEFT: - mBtnLeft = rawEvent->value; - break; - case BTN_RIGHT: - mBtnRight = rawEvent->value; - break; - case BTN_MIDDLE: - mBtnMiddle = rawEvent->value; - break; - case BTN_BACK: - mBtnBack = rawEvent->value; - break; - case BTN_SIDE: - mBtnSide = rawEvent->value; - break; - case BTN_FORWARD: - mBtnForward = rawEvent->value; - break; - case BTN_EXTRA: - mBtnExtra = rawEvent->value; - break; - case BTN_TASK: - mBtnTask = rawEvent->value; - break; - } - } -} - -uint32_t CursorButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnLeft) { - result |= AMOTION_EVENT_BUTTON_PRIMARY; - } - if (mBtnRight) { - result |= AMOTION_EVENT_BUTTON_SECONDARY; - } - if (mBtnMiddle) { - result |= AMOTION_EVENT_BUTTON_TERTIARY; - } - if (mBtnBack || mBtnSide) { - result |= AMOTION_EVENT_BUTTON_BACK; - } - if (mBtnForward || mBtnExtra) { - result |= AMOTION_EVENT_BUTTON_FORWARD; - } - return result; -} - - -// --- CursorMotionAccumulator --- - -CursorMotionAccumulator::CursorMotionAccumulator() { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::clearRelativeAxes() { - mRelX = 0; - mRelY = 0; -} - -void CursorMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_X: - mRelX = rawEvent->value; - break; - case REL_Y: - mRelY = rawEvent->value; - break; - } - } -} - -void CursorMotionAccumulator::finishSync() { - clearRelativeAxes(); -} - - -// --- CursorScrollAccumulator --- - -CursorScrollAccumulator::CursorScrollAccumulator() : - mHaveRelWheel(false), mHaveRelHWheel(false) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::configure(InputDevice* device) { - mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); - mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); -} - -void CursorScrollAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::clearRelativeAxes() { - mRelWheel = 0; - mRelHWheel = 0; -} - -void CursorScrollAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_WHEEL: - mRelWheel = rawEvent->value; - break; - case REL_HWHEEL: - mRelHWheel = rawEvent->value; - break; - } - } -} - -void CursorScrollAccumulator::finishSync() { - clearRelativeAxes(); -} - - -// --- TouchButtonAccumulator --- - -TouchButtonAccumulator::TouchButtonAccumulator() : - mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); -} - -void TouchButtonAccumulator::configure(InputDevice* device) { - mHaveBtnTouch = device->hasKey(BTN_TOUCH); - mHaveStylus = device->hasKey(BTN_TOOL_PEN) - || device->hasKey(BTN_TOOL_RUBBER) - || device->hasKey(BTN_TOOL_BRUSH) - || device->hasKey(BTN_TOOL_PENCIL) - || device->hasKey(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDevice* device) { - mBtnTouch = device->isKeyPressed(BTN_TOUCH); - mBtnStylus = device->isKeyPressed(BTN_STYLUS); - // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = - device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); - mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; -} - -void TouchButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_TOUCH: - mBtnTouch = rawEvent->value; - break; - case BTN_STYLUS: - mBtnStylus = rawEvent->value; - break; - case BTN_STYLUS2: - case BTN_0:// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = rawEvent->value; - break; - case BTN_TOOL_FINGER: - mBtnToolFinger = rawEvent->value; - break; - case BTN_TOOL_PEN: - mBtnToolPen = rawEvent->value; - break; - case BTN_TOOL_RUBBER: - mBtnToolRubber = rawEvent->value; - break; - case BTN_TOOL_BRUSH: - mBtnToolBrush = rawEvent->value; - break; - case BTN_TOOL_PENCIL: - mBtnToolPencil = rawEvent->value; - break; - case BTN_TOOL_AIRBRUSH: - mBtnToolAirbrush = rawEvent->value; - break; - case BTN_TOOL_MOUSE: - mBtnToolMouse = rawEvent->value; - break; - case BTN_TOOL_LENS: - mBtnToolLens = rawEvent->value; - break; - case BTN_TOOL_DOUBLETAP: - mBtnToolDoubleTap = rawEvent->value; - break; - case BTN_TOOL_TRIPLETAP: - mBtnToolTripleTap = rawEvent->value; - break; - case BTN_TOOL_QUADTAP: - mBtnToolQuadTap = rawEvent->value; - break; - } - } -} - -uint32_t TouchButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnStylus) { - result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; - } - if (mBtnStylus2) { - result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; - } - return result; -} - -int32_t TouchButtonAccumulator::getToolType() const { - if (mBtnToolMouse || mBtnToolLens) { - return AMOTION_EVENT_TOOL_TYPE_MOUSE; - } - if (mBtnToolRubber) { - return AMOTION_EVENT_TOOL_TYPE_ERASER; - } - if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { - return AMOTION_EVENT_TOOL_TYPE_FINGER; - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -bool TouchButtonAccumulator::isToolActive() const { - return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber - || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush - || mBtnToolMouse || mBtnToolLens - || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; -} - -bool TouchButtonAccumulator::isHovering() const { - return mHaveBtnTouch && !mBtnTouch; -} - -bool TouchButtonAccumulator::hasStylus() const { - return mHaveStylus; -} - - -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - - -// --- RawPointerData --- - -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} - -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { - float x = 0, y = 0; - uint32_t count = touchingIdBits.count(); - if (count) { - for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const Pointer& pointer = pointerForId(id); - x += pointer.x; - y += pointer.y; - } - x /= count; - y /= count; - } - *outX = x; - *outY = y; -} - - -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - - -// --- SingleTouchMotionAccumulator --- - -SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { - clearAbsoluteAxes(); -} - -void SingleTouchMotionAccumulator::reset(InputDevice* device) { - mAbsX = device->getAbsoluteAxisValue(ABS_X); - mAbsY = device->getAbsoluteAxisValue(ABS_Y); - mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); -} - -void SingleTouchMotionAccumulator::clearAbsoluteAxes() { - mAbsX = 0; - mAbsY = 0; - mAbsPressure = 0; - mAbsToolWidth = 0; - mAbsDistance = 0; - mAbsTiltX = 0; - mAbsTiltY = 0; -} - -void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - switch (rawEvent->code) { - case ABS_X: - mAbsX = rawEvent->value; - break; - case ABS_Y: - mAbsY = rawEvent->value; - break; - case ABS_PRESSURE: - mAbsPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAbsToolWidth = rawEvent->value; - break; - case ABS_DISTANCE: - mAbsDistance = rawEvent->value; - break; - case ABS_TILT_X: - mAbsTiltX = rawEvent->value; - break; - case ABS_TILT_Y: - mAbsTiltY = rawEvent->value; - break; - } - } -} - - -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mSlots(nullptr), - mSlotCount(0), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { - delete[] mSlots; -} - -void MultiTouchMotionAccumulator::configure(InputDevice* device, - size_t slotCount, bool usingSlotsProtocol) { - mSlotCount = slotCount; - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - - delete[] mSlots; - mSlots = new Slot[slotCount]; -} - -void MultiTouchMotionAccumulator::reset(InputDevice* device) { - // Unfortunately there is no way to read the initial contents of the slots. - // So when we reset the accumulator, we must assume they are all zeroes. - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), - ABS_MT_SLOT, &initialSlot); - if (status) { - ALOGD("Could not retrieve current multitouch slot index. status=%d", status); - initialSlot = -1; - } - clearSlots(initialSlot); - } else { - clearSlots(-1); - } -} - -void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { - if (mSlots) { - for (size_t i = 0; i < mSlotCount; i++) { - mSlots[i].clear(); - } - } - mCurrentSlot = initialSlot; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); - } -#endif - } else { - Slot* slot = &mSlots[mCurrentSlot]; - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot->mInUse = true; - slot->mAbsMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - slot->mInUse = true; - slot->mAbsMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - slot->mInUse = true; - slot->mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->mInUse = true; - slot->mAbsMTTouchMinor = rawEvent->value; - slot->mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot->mInUse = true; - slot->mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->mInUse = true; - slot->mAbsMTWidthMinor = rawEvent->value; - slot->mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot->mInUse = true; - slot->mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot->mInUse = false; - } else { - slot->mInUse = true; - slot->mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->mInUse = true; - slot->mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot->mInUse = true; - slot->mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->mInUse = true; - slot->mAbsMTToolType = rawEvent->value; - slot->mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - clearSlots(-1); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - - -// --- MultiTouchMotionAccumulator::Slot --- - -MultiTouchMotionAccumulator::Slot::Slot() { - clear(); -} - -void MultiTouchMotionAccumulator::Slot::clear() { - mInUse = false; - mHaveAbsMTTouchMinor = false; - mHaveAbsMTWidthMinor = false; - mHaveAbsMTToolType = false; - mAbsMTPositionX = 0; - mAbsMTPositionY = 0; - mAbsMTTouchMajor = 0; - mAbsMTTouchMinor = 0; - mAbsMTWidthMajor = 0; - mAbsMTWidthMinor = 0; - mAbsMTOrientation = 0; - mAbsMTTrackingId = -1; - mAbsMTPressure = 0; - mAbsMTDistance = 0; - mAbsMTToolType = 0; -} - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - - -// --- InputMapper --- - -InputMapper::InputMapper(InputDevice* device) : - mDevice(device), mContext(device->getContext()) { -} - -InputMapper::~InputMapper() { -} - -void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { - info->addSource(getSources()); -} - -void InputMapper::dump(std::string& dump) { -} - -void InputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { -} - -void InputMapper::reset(nsecs_t when) { -} - -void InputMapper::timeoutExpired(nsecs_t when) { -} - -int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return AKEY_STATE_UNKNOWN; -} - -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return false; -} - -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -} - -void InputMapper::cancelVibrate(int32_t token) { -} - -void InputMapper::cancelTouch(nsecs_t when) { -} - -int32_t InputMapper::getMetaState() { - return 0; -} - -void InputMapper::updateMetaState(int32_t keyCode) { -} - -void InputMapper::updateExternalStylusState(const StylusState& state) { - -} - -void InputMapper::fadePointer() { -} - -status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); -} - -void InputMapper::bumpGeneration() { - mDevice->bumpGeneration(); -} - -void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, - const RawAbsoluteAxisInfo& axis, const char* name) { - if (axis.valid) { - dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); - } else { - dump += StringPrintf(INDENT4 "%s: unknown range\n", name); - } -} - -void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { - dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); - dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); - dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); -} - -// --- SwitchInputMapper --- - -SwitchInputMapper::SwitchInputMapper(InputDevice* device) : - InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) { -} - -SwitchInputMapper::~SwitchInputMapper() { -} - -uint32_t SwitchInputMapper::getSources() { - return AINPUT_SOURCE_SWITCH; -} - -void SwitchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_SW: - processSwitch(rawEvent->code, rawEvent->value); - break; - - case EV_SYN: - if (rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } - } -} - -void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { - if (switchCode >= 0 && switchCode < 32) { - if (switchValue) { - mSwitchValues |= 1 << switchCode; - } else { - mSwitchValues &= ~(1 << switchCode); - } - mUpdatedSwitchMask |= 1 << switchCode; - } -} - -void SwitchInputMapper::sync(nsecs_t when) { - if (mUpdatedSwitchMask) { - uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, - mUpdatedSwitchMask); - getListener()->notifySwitch(&args); - - mUpdatedSwitchMask = 0; - } -} - -int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); -} - -void SwitchInputMapper::dump(std::string& dump) { - dump += INDENT2 "Switch Input Mapper:\n"; - dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); -} - -// --- VibratorInputMapper --- - -VibratorInputMapper::VibratorInputMapper(InputDevice* device) : - InputMapper(device), mVibrating(false) { -} - -VibratorInputMapper::~VibratorInputMapper() { -} - -uint32_t VibratorInputMapper::getSources() { - return 0; -} - -void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setVibrator(true); -} - -void VibratorInputMapper::process(const RawEvent* rawEvent) { - // TODO: Handle FF_STATUS, although it does not seem to be widely supported. -} - -void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -#if DEBUG_VIBRATOR - std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } - ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", - getDeviceId(), patternStr.c_str(), repeat, token); -#endif - - mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; - mRepeat = repeat; - mToken = token; - mIndex = -1; - - nextStep(); -} - -void VibratorInputMapper::cancelVibrate(int32_t token) { -#if DEBUG_VIBRATOR - ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); -#endif - - if (mVibrating && mToken == token) { - stopVibrating(); - } -} - -void VibratorInputMapper::timeoutExpired(nsecs_t when) { - if (mVibrating) { - if (when >= mNextStepTime) { - nextStep(); - } else { - getContext()->requestTimeoutAtTime(mNextStepTime); - } - } -} - -void VibratorInputMapper::nextStep() { - mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { - if (mRepeat < 0) { - // We are done. - stopVibrating(); - return; - } - mIndex = mRepeat; - } - - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); -#endif - getEventHub()->vibrate(getDeviceId(), duration); - } else { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); - } - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; - getContext()->requestTimeoutAtTime(mNextStepTime); -#if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); -#endif -} - -void VibratorInputMapper::stopVibrating() { - mVibrating = false; -#if DEBUG_VIBRATOR - ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); -} - -void VibratorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Vibrator Input Mapper:\n"; - dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); -} - - -// --- KeyboardInputMapper --- - -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, - uint32_t source, int32_t keyboardType) : - InputMapper(device), mSource(source), mKeyboardType(keyboardType) { -} - -KeyboardInputMapper::~KeyboardInputMapper() { -} - -uint32_t KeyboardInputMapper::getSources() { - return mSource; -} - -int32_t KeyboardInputMapper::getOrientation() { - if (mViewport) { - return mViewport->orientation; - } - return DISPLAY_ORIENTATION_0; -} - -int32_t KeyboardInputMapper::getDisplayId() { - if (mViewport) { - return mViewport->displayId; - } - return ADISPLAY_ID_NONE; -} - -void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); -} - -void KeyboardInputMapper::dump(std::string& dump) { - dump += INDENT2 "Keyboard Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); - dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); - dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -std::optional KeyboardInputMapper::findViewport( - nsecs_t when, const InputReaderConfiguration* config) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // No associated display defined, try to find default display if orientationAware. - if (mParameters.orientationAware) { - return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return std::nullopt; -} - -void KeyboardInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = findViewport(when, config); - } -} - -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const *property) { - int32_t mapped = 0; - if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } - } -} - -void KeyboardInputMapper::configureParameters() { - mParameters.orientationAware = false; - const PropertyMap& config = getDevice()->getConfiguration(); - config.tryGetProperty(String8("keyboard.orientationAware"), - mParameters.orientationAware); - - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } - - mParameters.handlesKeyRepeat = false; - config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), - mParameters.handlesKeyRepeat); -} - -void KeyboardInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); - dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", - toString(mParameters.handlesKeyRepeat)); -} - -void KeyboardInputMapper::reset(nsecs_t when) { - mMetaState = AMETA_NONE; - mDownTime = 0; - mKeyDowns.clear(); - mCurrentHidUsage = 0; - - resetLedState(); - - InputMapper::reset(when); -} - -void KeyboardInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); - } - break; - } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; - } - break; - } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } - } -} - -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE - || scanCode >= KEY_OK - || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) - || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, - int32_t usageCode) { - int32_t keyCode; - int32_t keyMetaState; - uint32_t policyFlags; - - if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, - &keyCode, &keyMetaState, &policyFlags)) { - keyCode = AKEYCODE_UNKNOWN; - keyMetaState = mMetaState; - policyFlags = 0; - } - - if (down) { - // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware) { - keyCode = rotateKeyCode(keyCode, getOrientation()); - } - - // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) - && mContext->shouldDropVirtualKey(when, - getDevice(), keyCode, scanCode)) { - return; - } - if (policyFlags & POLICY_FLAG_GESTURE) { - mDevice->cancelTouch(when); - } - - KeyDown keyDown; - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; - mKeyDowns.push_back(keyDown); - } - - mDownTime = when; - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); - } else { - // key was not actually down - ALOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().c_str(), keyCode, scanCode); - return; - } - } - - if (updateMetaStateIfNeeded(keyCode, down)) { - // If global meta state changed send it along with the key. - // If it has not changed then we'll use what keymap gave us, - // since key replacement logic might temporarily reset a few - // meta bits for given key. - keyMetaState = mMetaState; - } - - nsecs_t downTime = mDownTime; - - // Key down on external an keyboard should wake the device. - // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. - // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mParameters.handlesKeyRepeat) { - policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; - } - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); -} - -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { - size_t n = mKeyDowns.size(); - for (size_t i = 0; i < n; i++) { - if (mKeyDowns[i].scanCode == scanCode) { - return i; - } - } - return -1; -} - -int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); -} - -int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); -} - -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); -} - -int32_t KeyboardInputMapper::getMetaState() { - return mMetaState; -} - -void KeyboardInputMapper::updateMetaState(int32_t keyCode) { - updateMetaStateIfNeeded(keyCode, false); -} - -bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { - int32_t oldMetaState = mMetaState; - int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); - bool metaStateChanged = oldMetaState != newMetaState; - if (metaStateChanged) { - mMetaState = newMetaState; - updateLedState(false); - - getContext()->updateGlobalMetaState(); - } - - return metaStateChanged; -} - -void KeyboardInputMapper::resetLedState() { - initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); - initializeLedState(mNumLockLedState, ALED_NUM_LOCK); - initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); - - updateLedState(true); -} - -void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { - ledState.avail = getEventHub()->hasLed(getDeviceId(), led); - ledState.on = false; -} - -void KeyboardInputMapper::updateLedState(bool reset) { - updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, - AMETA_CAPS_LOCK_ON, reset); - updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, - AMETA_NUM_LOCK_ON, reset); - updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, - AMETA_SCROLL_LOCK_ON, reset); -} - -void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, - int32_t led, int32_t modifier, bool reset) { - if (ledState.avail) { - bool desiredState = (mMetaState & modifier) != 0; - if (reset || ledState.on != desiredState) { - getEventHub()->setLedState(getDeviceId(), led, desiredState); - ledState.on = desiredState; - } - } -} - -std::optional KeyboardInputMapper::getAssociatedDisplayId() { - if (mViewport) { - return std::make_optional(mViewport->displayId); - } - return std::nullopt; -} - -// --- CursorInputMapper --- - -CursorInputMapper::CursorInputMapper(InputDevice* device) : - InputMapper(device) { -} - -CursorInputMapper::~CursorInputMapper() { -} - -uint32_t CursorInputMapper::getSources() { - return mSource; -} - -void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mParameters.mode == Parameters::MODE_POINTER) { - float minX, minY, maxX, maxY; - if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); - } - } else { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); - } - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } -} - -void CursorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Cursor Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeVWheel())); - dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeHWheel())); - dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); - dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); - dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); - dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -void CursorInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - mCursorScrollAccumulator.configure(getDevice()); - - // Configure basic parameters. - configureParameters(); - - // Configure device mode. - switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: - // Should not happen during first time configuration. - ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; - [[fallthrough]]; - case Parameters::MODE_POINTER: - mSource = AINPUT_SOURCE_MOUSE; - mXPrecision = 1.0f; - mYPrecision = 1.0f; - mXScale = 1.0f; - mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - break; - case Parameters::MODE_NAVIGATION: - mSource = AINPUT_SOURCE_TRACKBALL; - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - break; - } - - mVWheelScale = 1.0f; - mHWheelScale = 1.0f; - } - - if ((!changes && config->pointerCapture) - || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { - if (config->pointerCapture) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; - mSource = AINPUT_SOURCE_MOUSE_RELATIVE; - // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else { - ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); - } - } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; - mSource = AINPUT_SOURCE_MOUSE; - } else { - ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); - } - } - bumpGeneration(); - if (changes) { - getDevice()->notifyReset(when); - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mOrientation = DISPLAY_ORIENTATION_0; - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } - } - - // Update the PointerController if viewports changed. - if (mParameters.mode == Parameters::MODE_POINTER) { - getPolicy()->obtainPointerController(getDeviceId()); - } - bumpGeneration(); - } -} - -void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; - String8 cursorModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { - if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; - } else if (cursorModeString != "pointer" && cursorModeString != "default") { - ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); - } - } - - mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { - mParameters.hasAssociatedDisplay = true; - } -} - -void CursorInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", - toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); -} - -void CursorInputMapper::reset(nsecs_t when) { - mButtonState = 0; - mDownTime = 0; - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mCursorButtonAccumulator.reset(getDevice()); - mCursorMotionAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void CursorInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorMotionAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void CursorInputMapper::sync(nsecs_t when) { - int32_t lastButtonState = mButtonState; - int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); - mButtonState = currentButtonState; - - bool wasDown = isPointerDown(lastButtonState); - bool down = isPointerDown(currentButtonState); - bool downChanged; - if (!wasDown && down) { - mDownTime = when; - downChanged = true; - } else if (wasDown && !down) { - downChanged = true; - } else { - downChanged = false; - } - nsecs_t downTime = mDownTime; - bool buttonsChanged = currentButtonState != lastButtonState; - int32_t buttonsPressed = currentButtonState & ~lastButtonState; - int32_t buttonsReleased = lastButtonState & ~currentButtonState; - - float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; - float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; - bool moved = deltaX != 0 || deltaY != 0; - - // Rotate delta according to orientation if needed. - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay - && (deltaX != 0.0f || deltaY != 0.0f)) { - rotateDelta(mOrientation, &deltaX, &deltaY); - } - - // Move the pointer. - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); - float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); - bool scrolled = vscroll != 0 || hscroll != 0; - - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - int32_t displayId; - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation( - PointerControllerInterface::PRESENTATION_POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); - } - - if (buttonsChanged) { - mPointerController->setButtonState(currentButtonState); - } - - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = mPointerController->getDisplayId(); - } else { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); - displayId = ADISPLAY_ID_NONE; - } - - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - - // Moving an external trackball or mouse should wake the device. - // We don't do this for internal cursor devices to prevent them from waking up - // the device in your pocket. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - // Synthesize key down from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - // Send motion event. - if (downChanged || moved || scrolled || buttonsChanged) { - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = lastButtonState; - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } else { - motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; - } - - if (buttonsReleased) { - BitSet32 released(buttonsReleased); - while (!released.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); - buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); - } - } - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - - if (buttonsPressed) { - BitSet32 pressed(buttonsPressed); - while (!pressed.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); - buttonState |= actionButton; - NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); - } - } - - ALOG_ASSERT(buttonState == currentButtonState); - - // Send hover move after UP to tell the application that the mouse is hovering now. - if (motionEventAction == AMOTION_EVENT_ACTION_UP - && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); - } - - // Send scroll events. - if (scrolled) { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - } - - // Synthesize key up from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - mCursorMotionAccumulator.finishSync(); - mCursorScrollAccumulator.finishSync(); -} - -int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); - } else { - return AKEY_STATE_UNKNOWN; - } -} - -void CursorInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -std::optional CursorInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - // If the device is orientationAware and not a mouse, - // it expects to dispatch events to any display - return std::make_optional(ADISPLAY_ID_NONE); - } - } - return std::nullopt; -} - -// --- RotaryEncoderInputMapper --- - -RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) : - InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { - mSource = AINPUT_SOURCE_ROTARY_ENCODER; -} - -RotaryEncoderInputMapper::~RotaryEncoderInputMapper() { -} - -uint32_t RotaryEncoderInputMapper::getSources() { - return mSource; -} - -void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { - float res = 0.0f; - if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { - ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); - } - if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), - mScalingFactor)) { - ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," - "default to 1.0!\n"); - mScalingFactor = 1.0f; - } - info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - res * mScalingFactor); - } -} - -void RotaryEncoderInputMapper::dump(std::string& dump) { - dump += INDENT2 "Rotary Encoder Input Mapper:\n"; - dump += StringPrintf(INDENT3 "HaveWheel: %s\n", - toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); -} - -void RotaryEncoderInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - if (!changes) { - mRotaryEncoderScrollAccumulator.configure(getDevice()); - } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; - } - } -} - -void RotaryEncoderInputMapper::reset(nsecs_t when) { - mRotaryEncoderScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { - mRotaryEncoderScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void RotaryEncoderInputMapper::sync(nsecs_t when) { - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); - bool scrolled = scroll != 0; - - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - - // Send motion event. - if (scrolled) { - int32_t metaState = mContext->getGlobalMetaState(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - - mRotaryEncoderScrollAccumulator.finishSync(); -} - -// --- TouchInputMapper --- - -TouchInputMapper::TouchInputMapper(InputDevice* device) : - InputMapper(device), - mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), - mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0), - mSurfaceOrientation(DISPLAY_ORIENTATION_0) { -} - -TouchInputMapper::~TouchInputMapper() { -} - -uint32_t TouchInputMapper::getSources() { - return mSource; -} - -void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mDeviceMode != DEVICE_MODE_DISABLED) { - info->addMotionRange(mOrientedRanges.x); - info->addMotionRange(mOrientedRanges.y); - info->addMotionRange(mOrientedRanges.pressure); - - if (mOrientedRanges.haveSize) { - info->addMotionRange(mOrientedRanges.size); - } - - if (mOrientedRanges.haveTouchSize) { - info->addMotionRange(mOrientedRanges.touchMajor); - info->addMotionRange(mOrientedRanges.touchMinor); - } - - if (mOrientedRanges.haveToolSize) { - info->addMotionRange(mOrientedRanges.toolMajor); - info->addMotionRange(mOrientedRanges.toolMinor); - } - - if (mOrientedRanges.haveOrientation) { - info->addMotionRange(mOrientedRanges.orientation); - } - - if (mOrientedRanges.haveDistance) { - info->addMotionRange(mOrientedRanges.distance); - } - - if (mOrientedRanges.haveTilt) { - info->addMotionRange(mOrientedRanges.tilt); - } - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - } - info->setButtonUnderPad(mParameters.hasButtonUnderPad); - } -} - -void TouchInputMapper::dump(std::string& dump) { - dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); - dumpParameters(dump); - dumpVirtualKeys(dump); - dumpRawPointerAxes(dump); - dumpCalibration(dump); - dumpAffineTransformation(dump); - dumpSurface(dump); - - dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); - dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); - dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); - dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); - dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); - dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); - dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); - dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); - dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); - dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); - dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); - dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); - dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); - - dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); - dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", - mLastRawState.rawPointerData.pointerCount); - for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { - const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " - "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " - "toolType=%d, isHovering=%s\n", i, - pointer.id, pointer.x, pointer.y, pointer.pressure, - pointer.touchMajor, pointer.touchMinor, - pointer.toolMajor, pointer.toolMinor, - pointer.orientation, pointer.tiltX, pointer.tiltY, pointer.distance, - pointer.toolType, toString(pointer.isHovering)); - } - - dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", mLastCookedState.buttonState); - dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", - mLastCookedState.cookedPointerData.pointerCount); - for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { - const PointerProperties& pointerProperties = - mLastCookedState.cookedPointerData.pointerProperties[i]; - const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " - "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, " - "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " - "toolType=%d, isHovering=%s\n", i, - pointerProperties.id, - pointerCoords.getX(), - pointerCoords.getY(), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), - pointerProperties.toolType, - toString(mLastCookedState.cookedPointerData.isHovering(i))); - } - - dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); - dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", - mExternalStylusFusionTimeout); - dump += INDENT3 "External Stylus State:\n"; - dumpStylusState(dump, mExternalStylusState); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); - dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", - mPointerXMovementScale); - dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", - mPointerYMovementScale); - dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", - mPointerXZoomScale); - dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", - mPointerYZoomScale); - dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", - mPointerGestureMaxSwipeWidth); - } -} - -const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { - switch (deviceMode) { - case DEVICE_MODE_DISABLED: - return "disabled"; - case DEVICE_MODE_DIRECT: - return "direct"; - case DEVICE_MODE_UNSCALED: - return "unscaled"; - case DEVICE_MODE_NAVIGATION: - return "navigation"; - case DEVICE_MODE_POINTER: - return "pointer"; - } - return "unknown"; -} - -void TouchInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - mConfig = *config; - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - - // Configure common accumulators. - mCursorScrollAccumulator.configure(getDevice()); - mTouchButtonAccumulator.configure(getDevice()); - - // Configure absolute axis information. - configureRawPointerAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { - // Update location calibration to reflect current settings - updateAffineTransformation(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - // Update pointer speed. - mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - } - - bool resetNeeded = false; - if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO - | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT - | InputReaderConfiguration::CHANGE_SHOW_TOUCHES - | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { - // Configure device sources, surface dimensions, orientation and - // scaling factors. - configureSurface(when, &resetNeeded); - } - - if (changes && resetNeeded) { - // Send reset, unless this is the first time the device has been configured, - // in which case the reader will call reset itself after all mappers are ready. - getDevice()->notifyReset(when); - } -} - -void TouchInputMapper::resolveExternalStylusPresence() { - std::vector devices; - mContext->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { - resetExternalStylus(); - } -} - -void TouchInputMapper::configureParameters() { - // Use the pointer presentation mode for devices that do not support distinct - // multitouch. The spot-based presentation relies on being able to accurately - // locate two or more fingers on the touch pad. - mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) - ? Parameters::GESTURE_MODE_SINGLE_TOUCH : Parameters::GESTURE_MODE_MULTI_TOUCH; - - String8 gestureModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), - gestureModeString)) { - if (gestureModeString == "single-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; - } else if (gestureModeString == "multi-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; - } else if (gestureModeString != "default") { - ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); - } - } - - if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { - // The device is a touch screen. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { - // The device is a pointing device like a track pad. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) - || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { - // The device is a cursor device with a touch pad attached. - // By default don't use the touch pad to move the pointer. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else { - // The device is a touch pad of unknown purpose. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } - - mParameters.hasButtonUnderPad= - getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); - - String8 deviceTypeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), - deviceTypeString)) { - if (deviceTypeString == "touchScreen") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (deviceTypeString == "touchPad") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else if (deviceTypeString == "touchNavigation") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; - } else if (deviceTypeString == "pointer") { - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (deviceTypeString != "default") { - ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); - } - } - - mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; - getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - mParameters.associatedDisplayIsExternal = false; - if (mParameters.orientationAware - || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - mParameters.hasAssociatedDisplay = true; - if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); - String8 uniqueDisplayId; - getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - uniqueDisplayId); - mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); - } - } - if (getDevice()->getAssociatedDisplayPort()) { - mParameters.hasAssociatedDisplay = true; - } - - // Initial downs on external touch devices should wake the device. - // Normally we don't do this for internal touch screens to prevent them from waking - // up in your pocket but you can enable it using the input device configuration. - mParameters.wake = getDevice()->isExternal(); - getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), - mParameters.wake); -} - -void TouchInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - - switch (mParameters.gestureMode) { - case Parameters::GESTURE_MODE_SINGLE_TOUCH: - dump += INDENT4 "GestureMode: single-touch\n"; - break; - case Parameters::GESTURE_MODE_MULTI_TOUCH: - dump += INDENT4 "GestureMode: multi-touch\n"; - break; - default: - assert(false); - } - - switch (mParameters.deviceType) { - case Parameters::DEVICE_TYPE_TOUCH_SCREEN: - dump += INDENT4 "DeviceType: touchScreen\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_PAD: - dump += INDENT4 "DeviceType: touchPad\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: - dump += INDENT4 "DeviceType: touchNavigation\n"; - break; - case Parameters::DEVICE_TYPE_POINTER: - dump += INDENT4 "DeviceType: pointer\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf( - INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n", - toString(mParameters.hasAssociatedDisplay), - toString(mParameters.associatedDisplayIsExternal), - mParameters.uniqueDisplayId.c_str()); - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); -} - -void TouchInputMapper::configureRawPointerAxes() { - mRawPointerAxes.clear(); -} - -void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { - dump += INDENT3 "Raw Touch Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); -} - -bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; -} - -/** - * Determine which DisplayViewport to use. - * 1. If display port is specified, return the matching viewport. If matching viewport not - * found, then return. - * 2. If a device has associated display, get the matching viewport by either unique id or by - * the display type (internal or external). - * 3. Otherwise, use a non-display viewport. - */ -std::optional TouchInputMapper::findViewport() { - if (mParameters.hasAssociatedDisplay) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // Check if uniqueDisplayId is specified in idc file. - if (!mParameters.uniqueDisplayId.empty()) { - return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); - } - - ViewportType viewportTypeToUse; - if (mParameters.associatedDisplayIsExternal) { - viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; - } else { - viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; - } - - std::optional viewport = - mConfig.getDisplayViewportByType(viewportTypeToUse); - if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { - ALOGW("Input device %s should be associated with external display, " - "fallback to internal one for the external viewport is not found.", - getDeviceName().c_str()); - viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return viewport; - } - - // No associated display, return a non-display viewport. - DisplayViewport newViewport; - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - newViewport.setNonDisplayViewport(rawWidth, rawHeight); - return std::make_optional(newViewport); -} - -void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { - int32_t oldDeviceMode = mDeviceMode; - - resolveExternalStylusPresence(); - - // Determine device mode. - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER - && mConfig.pointerGesturesEnabled) { - mSource = AINPUT_SOURCE_MOUSE; - mDeviceMode = DEVICE_MODE_POINTER; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - && mParameters.hasAssociatedDisplay) { - mSource = AINPUT_SOURCE_TOUCHSCREEN; - mDeviceMode = DEVICE_MODE_DIRECT; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - if (hasExternalStylus()) { - mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { - mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; - mDeviceMode = DEVICE_MODE_NAVIGATION; - } else { - mSource = AINPUT_SOURCE_TOUCHPAD; - mDeviceMode = DEVICE_MODE_UNSCALED; - } - - // Ensure we have valid X and Y axes. - if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { - ALOGW("Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Get associated display dimensions. - std::optional newViewport = findViewport(); - if (!newViewport) { - ALOGI("Touch device '%s' could not query the properties of its associated " - "display. The device will be inoperable until the display size " - "becomes available.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - - bool viewportChanged = mViewport != *newViewport; - if (viewportChanged) { - mViewport = *newViewport; - - if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { - // Convert rotated viewport to natural surface coordinates. - int32_t naturalLogicalWidth, naturalLogicalHeight; - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; - switch (mViewport.orientation) { - case DISPLAY_ORIENTATION_90: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; - - mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - - mSurfaceOrientation = mParameters.orientationAware ? - mViewport.orientation : DISPLAY_ORIENTATION_0; - } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mSurfaceWidth = rawWidth; - mSurfaceHeight = rawHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceOrientation = DISPLAY_ORIENTATION_0; - } - } - - // If moving between pointer modes, need to reset some state. - bool deviceModeChanged = mDeviceMode != oldDeviceMode; - if (deviceModeChanged) { - mOrientedRanges.clear(); - } - - // Create or update pointer controller if needed. - if (mDeviceMode == DEVICE_MODE_POINTER || - (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == nullptr || viewportChanged) { - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - } - } else { - mPointerController.clear(); - } - - if (viewportChanged || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " - "display id %d", - getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, - mSurfaceOrientation, mDeviceMode, mViewport.displayId); - - // Configure X and Y factors. - mXScale = float(mSurfaceWidth) / rawWidth; - mYScale = float(mSurfaceHeight) / rawHeight; - mXTranslate = -mSurfaceLeft; - mYTranslate = -mSurfaceTop; - mXPrecision = 1.0f / mXScale; - mYPrecision = 1.0f / mYScale; - - mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; - mOrientedRanges.x.source = mSource; - mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; - mOrientedRanges.y.source = mSource; - - configureVirtualKeys(); - - // Scale factor for terms that are not oriented in a particular axis. - // If the pixels are square then xScale == yScale otherwise we fake it - // by choosing an average. - mGeometricScale = avg(mXScale, mYScale); - - // Size of diagonal axis. - float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Size factors. - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - if (mRawPointerAxes.touchMajor.valid - && mRawPointerAxes.touchMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; - } else if (mRawPointerAxes.toolMajor.valid - && mRawPointerAxes.toolMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; - } else { - mSizeScale = 0.0f; - } - - mOrientedRanges.haveTouchSize = true; - mOrientedRanges.haveToolSize = true; - mOrientedRanges.haveSize = true; - - mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; - mOrientedRanges.touchMajor.source = mSource; - mOrientedRanges.touchMajor.min = 0; - mOrientedRanges.touchMajor.max = diagonalSize; - mOrientedRanges.touchMajor.flat = 0; - mOrientedRanges.touchMajor.fuzz = 0; - mOrientedRanges.touchMajor.resolution = 0; - - mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; - mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; - - mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; - mOrientedRanges.toolMajor.source = mSource; - mOrientedRanges.toolMajor.min = 0; - mOrientedRanges.toolMajor.max = diagonalSize; - mOrientedRanges.toolMajor.flat = 0; - mOrientedRanges.toolMajor.fuzz = 0; - mOrientedRanges.toolMajor.resolution = 0; - - mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; - mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; - - mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; - mOrientedRanges.size.source = mSource; - mOrientedRanges.size.min = 0; - mOrientedRanges.size.max = 1.0; - mOrientedRanges.size.flat = 0; - mOrientedRanges.size.fuzz = 0; - mOrientedRanges.size.resolution = 0; - } else { - mSizeScale = 0.0f; - } - - // Pressure factors. - mPressureScale = 0; - float pressureMax = 1.0; - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL - || mCalibration.pressureCalibration - == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { - if (mCalibration.havePressureScale) { - mPressureScale = mCalibration.pressureScale; - pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; - } else if (mRawPointerAxes.pressure.valid - && mRawPointerAxes.pressure.maxValue != 0) { - mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; - } - } - - mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; - mOrientedRanges.pressure.source = mSource; - mOrientedRanges.pressure.min = 0; - mOrientedRanges.pressure.max = pressureMax; - mOrientedRanges.pressure.flat = 0; - mOrientedRanges.pressure.fuzz = 0; - mOrientedRanges.pressure.resolution = 0; - - // Tilt - mTiltXCenter = 0; - mTiltXScale = 0; - mTiltYCenter = 0; - mTiltYScale = 0; - mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; - if (mHaveTilt) { - mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, - mRawPointerAxes.tiltX.maxValue); - mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, - mRawPointerAxes.tiltY.maxValue); - mTiltXScale = M_PI / 180; - mTiltYScale = M_PI / 180; - - mOrientedRanges.haveTilt = true; - - mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; - mOrientedRanges.tilt.source = mSource; - mOrientedRanges.tilt.min = 0; - mOrientedRanges.tilt.max = M_PI_2; - mOrientedRanges.tilt.flat = 0; - mOrientedRanges.tilt.fuzz = 0; - mOrientedRanges.tilt.resolution = 0; - } - - // Orientation - mOrientationScale = 0; - if (mHaveTilt) { - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI; - mOrientedRanges.orientation.max = M_PI; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } else if (mCalibration.orientationCalibration != - Calibration::ORIENTATION_CALIBRATION_NONE) { - if (mCalibration.orientationCalibration - == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { - if (mRawPointerAxes.orientation.valid) { - if (mRawPointerAxes.orientation.maxValue > 0) { - mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; - } else if (mRawPointerAxes.orientation.minValue < 0) { - mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; - } else { - mOrientationScale = 0; - } - } - } - - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } - - // Distance - mDistanceScale = 0; - if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { - if (mCalibration.distanceCalibration - == Calibration::DISTANCE_CALIBRATION_SCALED) { - if (mCalibration.haveDistanceScale) { - mDistanceScale = mCalibration.distanceScale; - } else { - mDistanceScale = 1.0f; - } - } - - mOrientedRanges.haveDistance = true; - - mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; - mOrientedRanges.distance.source = mSource; - mOrientedRanges.distance.min = - mRawPointerAxes.distance.minValue * mDistanceScale; - mOrientedRanges.distance.max = - mRawPointerAxes.distance.maxValue * mDistanceScale; - mOrientedRanges.distance.flat = 0; - mOrientedRanges.distance.fuzz = - mRawPointerAxes.distance.fuzz * mDistanceScale; - mOrientedRanges.distance.resolution = 0; - } - - // Compute oriented precision, scales and ranges. - // Note that the maximum value reported is an inclusive maximum value so it is one - // unit less than the total width or height of surface. - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - case DISPLAY_ORIENTATION_270: - mOrientedXPrecision = mYPrecision; - mOrientedYPrecision = mXPrecision; - - mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - - mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; - break; - - default: - mOrientedXPrecision = mXPrecision; - mOrientedYPrecision = mYPrecision; - - mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - - mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; - break; - } - - // Location - updateAffineTransformation(); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Scale movements such that one whole swipe of the touch pad covers a - // given area relative to the diagonal size of the display when no acceleration - // is applied. - // Assume that the touch pad has a square aspect ratio such that movements in - // X and Y of the same number of raw units cover the same physical distance. - mPointerXMovementScale = mConfig.pointerGestureMovementSpeedRatio - * displayDiagonal / rawDiagonal; - mPointerYMovementScale = mPointerXMovementScale; - - // Scale zooms to cover a smaller range of the display than movements do. - // This value determines the area around the pointer that is affected by freeform - // pointer gestures. - mPointerXZoomScale = mConfig.pointerGestureZoomSpeedRatio - * displayDiagonal / rawDiagonal; - mPointerYZoomScale = mPointerXZoomScale; - - // Max width between pointers to detect a swipe gesture is more than some fraction - // of the diagonal axis of the touch pad. Touches that are wider than this are - // translated into freeform gestures. - mPointerGestureMaxSwipeWidth = - mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - - // Abort current pointer usages because the state has changed. - abortPointerUsage(when, 0 /*policyFlags*/); - } - - // Inform the dispatcher about the changes. - *outResetNeeded = true; - bumpGeneration(); - } -} - -void TouchInputMapper::dumpSurface(std::string& dump) { - dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); - dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); - dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); - dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); - dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); -} - -void TouchInputMapper::configureVirtualKeys() { - std::vector virtualKeyDefinitions; - getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); - - mVirtualKeys.clear(); - - if (virtualKeyDefinitions.size() == 0) { - return; - } - - int32_t touchScreenLeft = mRawPointerAxes.x.minValue; - int32_t touchScreenTop = mRawPointerAxes.y.minValue; - int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); - int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); - - for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { - VirtualKey virtualKey; - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - int32_t dummyKeyMetaState; - uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, - &keyCode, &dummyKeyMetaState, &flags)) { - ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", - virtualKey.scanCode); - continue; // drop the key - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; - mVirtualKeys.push_back(virtualKey); - } -} - -void TouchInputMapper::dumpVirtualKeys(std::string& dump) { - if (!mVirtualKeys.empty()) { - dump += INDENT3 "Virtual Keys:\n"; - - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; - dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, - virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const PropertyMap& in = getDevice()->getConfiguration(); - Calibration& out = mCalibration; - - // Size - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { - if (sizeCalibrationString == "none") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } else if (sizeCalibrationString == "geometric") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } else if (sizeCalibrationString == "diameter") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; - } else if (sizeCalibrationString == "box") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; - } else if (sizeCalibrationString == "area") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; - } else if (sizeCalibrationString != "default") { - ALOGW("Invalid value for touch.size.calibration: '%s'", - sizeCalibrationString.string()); - } - } - - out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), - out.sizeScale); - out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), - out.sizeBias); - out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), - out.sizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { - if (pressureCalibrationString == "none") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } else if (pressureCalibrationString == "physical") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } else if (pressureCalibrationString == "amplitude") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else if (pressureCalibrationString != "default") { - ALOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); - } - } - - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), - out.pressureScale); - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { - if (orientationCalibrationString == "none") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } else if (orientationCalibrationString == "interpolated") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else if (orientationCalibrationString == "vector") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; - } else if (orientationCalibrationString != "default") { - ALOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); - } - } - - // Distance - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; - String8 distanceCalibrationString; - if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { - if (distanceCalibrationString == "none") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } else if (distanceCalibrationString == "scaled") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } else if (distanceCalibrationString != "default") { - ALOGW("Invalid value for touch.distance.calibration: '%s'", - distanceCalibrationString.string()); - } - } - - out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), - out.distanceScale); - - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; - String8 coverageCalibrationString; - if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { - if (coverageCalibrationString == "none") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } else if (coverageCalibrationString == "box") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; - } else if (coverageCalibrationString != "default") { - ALOGW("Invalid value for touch.coverage.calibration: '%s'", - coverageCalibrationString.string()); - } - } -} - -void TouchInputMapper::resolveCalibration() { - // Size - if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } - } else { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } - - // Pressure - if (mRawPointerAxes.pressure.valid) { - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } - } else { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } - - // Orientation - if (mRawPointerAxes.orientation.valid) { - if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } - } else { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } - - // Distance - if (mRawPointerAxes.distance.valid) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } - } else { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } - - // Coverage - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { - mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } -} - -void TouchInputMapper::dumpCalibration(std::string& dump) { - dump += INDENT3 "Calibration:\n"; - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump += INDENT4 "touch.size.calibration: none\n"; - break; - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - dump += INDENT4 "touch.size.calibration: geometric\n"; - break; - case Calibration::SIZE_CALIBRATION_DIAMETER: - dump += INDENT4 "touch.size.calibration: diameter\n"; - break; - case Calibration::SIZE_CALIBRATION_BOX: - dump += INDENT4 "touch.size.calibration: box\n"; - break; - case Calibration::SIZE_CALIBRATION_AREA: - dump += INDENT4 "touch.size.calibration: area\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveSizeScale) { - dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", - mCalibration.sizeScale); - } - - if (mCalibration.haveSizeBias) { - dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", - mCalibration.sizeBias); - } - - if (mCalibration.haveSizeIsSummed) { - dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", - toString(mCalibration.sizeIsSummed)); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump += INDENT4 "touch.pressure.calibration: none\n"; - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump += INDENT4 "touch.pressure.calibration: physical\n"; - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump += INDENT4 "touch.pressure.calibration: amplitude\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.havePressureScale) { - dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", - mCalibration.pressureScale); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump += INDENT4 "touch.orientation.calibration: none\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump += INDENT4 "touch.orientation.calibration: interpolated\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: - dump += INDENT4 "touch.orientation.calibration: vector\n"; - break; - default: - ALOG_ASSERT(false); - } - - // Distance - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_NONE: - dump += INDENT4 "touch.distance.calibration: none\n"; - break; - case Calibration::DISTANCE_CALIBRATION_SCALED: - dump += INDENT4 "touch.distance.calibration: scaled\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveDistanceScale) { - dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", - mCalibration.distanceScale); - } - - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_NONE: - dump += INDENT4 "touch.coverage.calibration: none\n"; - break; - case Calibration::COVERAGE_CALIBRATION_BOX: - dump += INDENT4 "touch.coverage.calibration: box\n"; - break; - default: - ALOG_ASSERT(false); - } -} - -void TouchInputMapper::dumpAffineTransformation(std::string& dump) { - dump += INDENT3 "Affine Transformation:\n"; - - dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); - dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); - dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); - dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); - dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); - dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); -} - -void TouchInputMapper::updateAffineTransformation() { - mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), - mSurfaceOrientation); -} - -void TouchInputMapper::reset(nsecs_t when) { - mCursorButtonAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - mTouchButtonAccumulator.reset(getDevice()); - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mRawStatesPending.clear(); - mCurrentRawState.clear(); - mCurrentCookedState.clear(); - mLastRawState.clear(); - mLastCookedState.clear(); - mPointerUsage = POINTER_USAGE_NONE; - mSentHoverEnter = false; - mHavePointerIds = false; - mCurrentMotionAborted = false; - mDownTime = 0; - - mCurrentVirtualKey.down = false; - - mPointerGesture.reset(); - mPointerSimple.reset(); - resetExternalStylus(); - - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } - - InputMapper::reset(when); -} - -void TouchInputMapper::resetExternalStylus() { - mExternalStylusState.clear(); - mExternalStylusId = -1; - mExternalStylusFusionTimeout = LLONG_MAX; - mExternalStylusDataPending = false; -} - -void TouchInputMapper::clearStylusDataPendingFlags() { - mExternalStylusDataPending = false; - mExternalStylusFusionTimeout = LLONG_MAX; -} - -void TouchInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void TouchInputMapper::sync(nsecs_t when) { - const RawState* last = mRawStatesPending.empty() ? - &mCurrentRawState : &mRawStatesPending.back(); - - // Push a new state. - mRawStatesPending.emplace_back(); - - RawState* next = &mRawStatesPending.back(); - next->clear(); - next->when = when; - - // Sync button state. - next->buttonState = mTouchButtonAccumulator.getButtonState() - | mCursorButtonAccumulator.getButtonState(); - - // Sync scroll - next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); - next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); - mCursorScrollAccumulator.finishSync(); - - // Sync touch - syncTouch(when, next); - - // Assign pointer ids. - if (!mHavePointerIds) { - assignPointerIds(last, next); - } - -#if DEBUG_RAW_EVENTS - ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x", - last->rawPointerData.pointerCount, - next->rawPointerData.pointerCount, - last->rawPointerData.touchingIdBits.value, - next->rawPointerData.touchingIdBits.value, - last->rawPointerData.hoveringIdBits.value, - next->rawPointerData.hoveringIdBits.value); -#endif - - processRawTouches(false /*timeout*/); -} - -void TouchInputMapper::processRawTouches(bool timeout) { - if (mDeviceMode == DEVICE_MODE_DISABLED) { - // Drop all input if the device is disabled. - mCurrentRawState.clear(); - mRawStatesPending.clear(); - return; - } - - // Drain any pending touch states. The invariant here is that the mCurrentRawState is always - // valid and must go through the full cook and dispatch cycle. This ensures that anything - // touching the current state will only observe the events that have been dispatched to the - // rest of the pipeline. - const size_t N = mRawStatesPending.size(); - size_t count; - for(count = 0; count < N; count++) { - const RawState& next = mRawStatesPending[count]; - - // A failure to assign the stylus id means that we're waiting on stylus data - // and so should defer the rest of the pipeline. - if (assignExternalStylusId(next, timeout)) { - break; - } - - // All ready to go. - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); - if (mCurrentRawState.when < mLastRawState.when) { - mCurrentRawState.when = mLastRawState.when; - } - cookAndDispatch(mCurrentRawState.when); - } - if (count != 0) { - mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); - } - - if (mExternalStylusDataPending) { - if (timeout) { - nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, synthesizing event with new stylus data"); -#endif - cookAndDispatch(when); - } else if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::cookAndDispatch(nsecs_t when) { - // Always start with a clean state. - mCurrentCookedState.clear(); - - // Apply stylus buttons to current raw state. - applyExternalStylusButtonState(when); - - // Handle policy on initial down or hover events. - bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 - && mCurrentRawState.rawPointerData.pointerCount != 0; - - uint32_t policyFlags = 0; - bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; - if (initialDown || buttonsPressed) { - // If this is a touch screen, hide the pointer on an initial down. - if (mDeviceMode == DEVICE_MODE_DIRECT) { - getContext()->fadePointer(); - } - - if (mParameters.wake) { - policyFlags |= POLICY_FLAG_WAKE; - } - } - - // Consume raw off-screen touches before cooking pointer data. - // If touches are consumed, subsequent code will not receive any pointer data. - if (consumeRawTouches(when, policyFlags)) { - mCurrentRawState.rawPointerData.clear(); - } - - // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure - // with cooked pointer data that has the same ids and indices as the raw data. - // The following code can use either the raw or cooked data, as needed. - cookPointerData(); - - // Apply stylus pressure to current cooked state. - applyExternalStylusTouchState(when); - - // Synthesize key down from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, - mLastCookedState.buttonState, mCurrentCookedState.buttonState); - - // Dispatch the touches either directly or by translation through a pointer on screen. - if (mDeviceMode == DEVICE_MODE_POINTER) { - for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); - !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mCurrentCookedState.fingerIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { - mCurrentCookedState.mouseIdBits.markBit(id); - } - } - for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); - !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } - } - - // Stylus takes precedence over all tools, then mouse, then finger. - PointerUsage pointerUsage = mPointerUsage; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - mCurrentCookedState.mouseIdBits.clear(); - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_STYLUS; - } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_MOUSE; - } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || - isPointerDown(mCurrentRawState.buttonState)) { - pointerUsage = POINTER_USAGE_GESTURES; - } - - dispatchPointerUsage(when, policyFlags, pointerUsage); - } else { - if (mDeviceMode == DEVICE_MODE_DIRECT - && mConfig.showTouches && mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, - mViewport.displayId); - } - - if (!mCurrentMotionAborted) { - dispatchButtonRelease(when, policyFlags); - dispatchHoverExit(when, policyFlags); - dispatchTouches(when, policyFlags); - dispatchHoverEnterAndMove(when, policyFlags); - dispatchButtonPress(when, policyFlags); - } - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentMotionAborted = false; - } - } - - // Synthesize key up from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, - mLastCookedState.buttonState, mCurrentCookedState.buttonState); - - // Clear some transient state. - mCurrentRawState.rawVScroll = 0; - mCurrentRawState.rawHScroll = 0; - - // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); -} - -void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; - } -} - -void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { - CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; - const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; - - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - - PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } - } -} - -bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { - if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { - return false; - } - - const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 - && state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { -#if DEBUG_STYLUS_FUSION - ALOGD("Have both stylus and touch data, beginning fusion"); -#endif - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, assuming touch is not a stylus."); -#endif - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } -#if DEBUG_STYLUS_FUSION - ALOGD("No stylus data but stylus is connected, requesting timeout " - "(%" PRId64 "ms)", mExternalStylusFusionTimeout); -#endif - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } - } - - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && - !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { -#if DEBUG_STYLUS_FUSION - ALOGD("Stylus pointer is going up"); -#endif - mExternalStylusId = -1; - } - - return false; -} - -void TouchInputMapper::timeoutExpired(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - if (mPointerUsage == POINTER_USAGE_GESTURES) { - dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); - } - } else if (mDeviceMode == DEVICE_MODE_DIRECT) { - if (mExternalStylusFusionTimeout < when) { - processRawTouches(true /*timeout*/); - } else if (mExternalStylusFusionTimeout != LLONG_MAX) { - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::updateExternalStylusState(const StylusState& state) { - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. - mExternalStylusDataPending = true; - processRawTouches(false /*timeout*/); - } -} - -bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { - // Check for release of a virtual key. - if (mCurrentVirtualKey.down) { - if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer went up while virtual key was down. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - return true; - } - - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation but do not consume the touch yet. - // This is useful when the user swipes through from the virtual key area - // into the main display surface. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED); - } - } - - if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() - && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer just went down. Check for virtual key press or off-screen touches. - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (!isPointInsideSurface(pointer.x, pointer.y)) { - // If exactly one pointer went down, check for virtual key hit. - // Otherwise we will drop the entire stroke. - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey) { - mCurrentVirtualKey.down = true; - mCurrentVirtualKey.downTime = when; - mCurrentVirtualKey.keyCode = virtualKey->keyCode; - mCurrentVirtualKey.scanCode = virtualKey->scanCode; - mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey( - when, getDevice(), virtualKey->keyCode, virtualKey->scanCode); - - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, - mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - } - } - return true; - } - } - - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch within the screen area. The idea is to filter out stray - // virtual key presses when interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mConfig.virtualKeyQuietTime > 0 && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); - } - return false; -} - -void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - int32_t keyCode = mCurrentVirtualKey.keyCode; - int32_t scanCode = mCurrentVirtualKey.scanCode; - nsecs_t downTime = mCurrentVirtualKey.downTime; - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, - policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - getListener()->notifyKey(&args); -} - -void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - if (!currentIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mCurrentMotionAborted = true; - } -} - -void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - if (currentIdBits == lastIdBits) { - if (!currentIdBits.isEmpty()) { - // No pointer id changes so this is a move event. - // The listener takes care of batching moves so we don't have to deal with that here. - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } else { - // 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 moveIdBits(lastIdBits.value & currentIdBits.value); - BitSet32 dispatchedIdBits(lastIdBits.value); - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool moveNeeded = updateMovedPointers( - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - moveIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - - // Dispatch pointer up events. - while (!upIdBits.isEmpty()) { - uint32_t upId = upIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - dispatchedIdBits.clearBit(upId); - } - - // 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 && !moveIdBits.isEmpty()) { - ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - - // Dispatch pointer down events using the new pointer locations. - while (!downIdBits.isEmpty()) { - uint32_t downId = downIdBits.clearFirstMarkedBit(); - dispatchedIdBits.markBit(downId); - - if (dispatchedIdBits.count() == 1) { - // First pointer is going down. Set down time. - mDownTime = when; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } -} - -void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { - if (mSentHoverEnter && - (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() - || !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { - int32_t metaState = getContext()->getGlobalMetaState(); - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastCookedState.buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = false; - } -} - -void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { - if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() - && !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - if (!mSentHoverEnter) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, - 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = true; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { - BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!releasedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); - buttonState &= ~actionButton; - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { - BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!pressedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); - buttonState |= actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { - if (!cookedPointerData.touchingIdBits.isEmpty()) { - return cookedPointerData.touchingIdBits; - } - return cookedPointerData.hoveringIdBits; -} - -void TouchInputMapper::cookPointerData() { - uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; - - mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; - mCurrentCookedState.cookedPointerData.hoveringIdBits = - mCurrentRawState.rawPointerData.hoveringIdBits; - mCurrentCookedState.cookedPointerData.touchingIdBits = - mCurrentRawState.rawPointerData.touchingIdBits; - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentCookedState.buttonState = 0; - } else { - mCurrentCookedState.buttonState = mCurrentRawState.buttonState; - } - - // Walk through the the active pointers and map device coordinates onto - // surface coordinates and adjust for display orientation. - for (uint32_t i = 0; i < currentPointerCount; i++) { - const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; - - // Size - float touchMajor, touchMinor, toolMajor, toolMinor, size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - case Calibration::SIZE_CALIBRATION_DIAMETER: - case Calibration::SIZE_CALIBRATION_BOX: - case Calibration::SIZE_CALIBRATION_AREA: - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { - touchMajor = in.touchMajor; - touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - toolMajor = in.toolMajor; - toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.touchMinor.valid - ? avg(in.touchMajor, in.touchMinor) : in.touchMajor; - } else if (mRawPointerAxes.touchMajor.valid) { - toolMajor = touchMajor = in.touchMajor; - toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid - ? in.touchMinor : in.touchMajor; - size = mRawPointerAxes.touchMinor.valid - ? avg(in.touchMajor, in.touchMinor) : in.touchMajor; - } else if (mRawPointerAxes.toolMajor.valid) { - touchMajor = toolMajor = in.toolMajor; - touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid - ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.toolMinor.valid - ? avg(in.toolMajor, in.toolMinor) : in.toolMajor; - } else { - ALOG_ASSERT(false, "No touch or tool axes. " - "Size calibration should have been resolved to NONE."); - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - } - - if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { - uint32_t touchingCount = - mCurrentRawState.rawPointerData.touchingIdBits.count(); - if (touchingCount > 1) { - touchMajor /= touchingCount; - touchMinor /= touchingCount; - toolMajor /= touchingCount; - toolMinor /= touchingCount; - size /= touchingCount; - } - } - - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { - touchMajor *= mGeometricScale; - touchMinor *= mGeometricScale; - toolMajor *= mGeometricScale; - toolMinor *= mGeometricScale; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { - touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; - touchMinor = touchMajor; - toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; - toolMinor = toolMajor; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { - touchMinor = touchMajor; - toolMinor = toolMajor; - } - - mCalibration.applySizeScaleAndBias(&touchMajor); - mCalibration.applySizeScaleAndBias(&touchMinor); - mCalibration.applySizeScaleAndBias(&toolMajor); - mCalibration.applySizeScaleAndBias(&toolMinor); - size *= mSizeScale; - break; - default: - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - break; - } - - // Pressure - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = in.pressure * mPressureScale; - break; - default: - pressure = in.isHovering ? 0 : 1; - break; - } - - // Tilt and Orientation - float tilt; - float orientation; - if (mHaveTilt) { - float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; - float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); - tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); - } else { - tilt = 0; - - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mOrientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float confidence = hypotf(c1, c2); - float scale = 1.0f + confidence / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; - } else { - orientation = 0; - } - break; - } - default: - orientation = 0; - } - } - - // Distance - float distance; - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_SCALED: - distance = in.distance * mDistanceScale; - break; - default: - distance = 0; - } - - // Coverage - int32_t rawLeft, rawTop, rawRight, rawBottom; - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_BOX: - rawLeft = (in.toolMinor & 0xffff0000) >> 16; - rawRight = in.toolMinor & 0x0000ffff; - rawBottom = in.toolMajor & 0x0000ffff; - rawTop = (in.toolMajor & 0xffff0000) >> 16; - break; - default: - rawLeft = rawTop = rawRight = rawBottom = 0; - break; - } - - // Adjust X,Y coords for device calibration - // TODO: Adjust coverage coords? - float xTransformed = in.x, yTransformed = in.y; - mAffineTransform.applyTo(xTransformed, yTransformed); - - // Adjust X, Y, and coverage coords for surface orientation. - float x, y; - float left, top, right, bottom; - - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; - left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; - top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; - orientation -= M_PI_2; - if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { - orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; - y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; - left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; - right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; - bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; - top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; - orientation -= M_PI; - if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { - orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; - y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; - right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; - bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - orientation += M_PI_2; - if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) { - orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - default: - x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - break; - } - - // Write output coords. - PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; - out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); - out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); - out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); - } else { - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); - } - - // Write output properties. - PointerProperties& properties = - mCurrentCookedState.cookedPointerData.pointerProperties[i]; - uint32_t id = in.id; - properties.clear(); - properties.id = id; - properties.toolType = in.toolType; - - // Write id index. - mCurrentCookedState.cookedPointerData.idToIndex[id] = i; - } -} - -void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, - PointerUsage pointerUsage) { - if (pointerUsage != mPointerUsage) { - abortPointerUsage(when, policyFlags); - mPointerUsage = pointerUsage; - } - - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); - break; - case POINTER_USAGE_STYLUS: - dispatchPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - dispatchPointerMouse(when, policyFlags); - break; - default: - break; - } -} - -void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - abortPointerGestures(when, policyFlags); - break; - case POINTER_USAGE_STYLUS: - abortPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - abortPointerMouse(when, policyFlags); - break; - default: - break; - } - - mPointerUsage = POINTER_USAGE_NONE; -} - -void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, - bool isTimeout) { - // Update current gesture coordinates. - bool cancelPreviousGesture, finishPreviousGesture; - bool sendEvents = preparePointerGestures(when, - &cancelPreviousGesture, &finishPreviousGesture, isTimeout); - if (!sendEvents) { - return; - } - if (finishPreviousGesture) { - cancelPreviousGesture = false; - } - - // Update the pointer presentation and spots. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - if (finishPreviousGesture || cancelPreviousGesture) { - mPointerController->clearSpots(); - } - - if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, - mPointerController->getDisplayId()); - } - } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - } - - // Show or hide the pointer if needed. - switch (mPointerGesture.currentGestureMode) { - case PointerGesture::NEUTRAL: - case PointerGesture::QUIET: - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH - && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { - // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); - } - break; - case PointerGesture::TAP: - case PointerGesture::TAP_DRAG: - case PointerGesture::BUTTON_CLICK_OR_DRAG: - case PointerGesture::HOVER: - case PointerGesture::PRESS: - case PointerGesture::SWIPE: - // Unfade the pointer when the current gesture manipulates the - // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - break; - case PointerGesture::FREEFORM: - // Fade the pointer when the current gesture manipulates a different - // area and there are spots to guide the user experience. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - break; - } - - // Send events! - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP - || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG - || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG - || mPointerGesture.currentGestureMode == PointerGesture::PRESS - || mPointerGesture.currentGestureMode == PointerGesture::SWIPE - || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; - bool moveNeeded = false; - if (down && !cancelPreviousGesture && !finishPreviousGesture - && !mPointerGesture.lastGestureIdBits.isEmpty() - && !mPointerGesture.currentGestureIdBits.isEmpty()) { - BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value - & mPointerGesture.lastGestureIdBits.value); - moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - movedGestureIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - } - - // Send motion events for all pointers that went up or were canceled. - BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); - if (!dispatchedGestureIdBits.isEmpty()) { - if (cancelPreviousGesture) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - - dispatchedGestureIdBits.clear(); - } else { - BitSet32 upGestureIdBits; - if (finishPreviousGesture) { - upGestureIdBits = dispatchedGestureIdBits; - } else { - upGestureIdBits.value = dispatchedGestureIdBits.value - & ~mPointerGesture.currentGestureIdBits.value; - } - while (!upGestureIdBits.isEmpty()) { - uint32_t id = upGestureIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - dispatchedGestureIdBits, id, - 0, 0, mPointerGesture.downTime); - - dispatchedGestureIdBits.clearBit(id); - } - } - } - - // Send motion events for all pointers that moved. - if (moveNeeded) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - } - - // Send motion events for all pointers that went down. - if (down) { - BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value - & ~dispatchedGestureIdBits.value); - while (!downGestureIdBits.isEmpty()) { - uint32_t id = downGestureIdBits.clearFirstMarkedBit(); - dispatchedGestureIdBits.markBit(id); - - if (dispatchedGestureIdBits.count() == 1) { - mPointerGesture.downTime = when; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - dispatchedGestureIdBits, id, - 0, 0, mPointerGesture.downTime); - } - } - - // Send motion events for hover. - if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); - } else if (dispatchedGestureIdBits.isEmpty() - && !mPointerGesture.lastGestureIdBits.isEmpty()) { - // Synthesize a hover move event after all pointers go up to indicate that - // the pointer is hovering again even if the user is not currently touching - // the touch pad. This ensures that a view will receive a fresh hover enter - // event after a tap. - float x, y; - mPointerController->getPosition(&x, &y); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - PointerCoords pointerCoords; - pointerCoords.clear(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - - const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Update state. - mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; - if (!down) { - mPointerGesture.lastGestureIdBits.clear(); - } else { - mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; - for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - mPointerGesture.lastGestureProperties[index].copyFrom( - mPointerGesture.currentGestureProperties[index]); - mPointerGesture.lastGestureCoords[index].copyFrom( - mPointerGesture.currentGestureCoords[index]); - mPointerGesture.lastGestureIdToIndex[id] = index; - } - } -} - -void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { - // Cancel previously dispatches pointers. - if (!mPointerGesture.lastGestureIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } - - // Reset the current pointer gesture. - mPointerGesture.reset(); - mPointerVelocityControl.reset(); - - // Remove any current spots. - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } -} - -bool TouchInputMapper::preparePointerGestures(nsecs_t when, - bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) { - *outCancelPreviousGesture = false; - *outFinishPreviousGesture = false; - - // Handle TAP timeout. - if (isTimeout) { -#if DEBUG_GESTURES - ALOGD("Gestures: Processing timeout"); -#endif - - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - // The tap/drag timeout has not yet expired. - getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime - + mConfig.pointerGestureTapDragInterval); - } else { - // The tap is finished. -#if DEBUG_GESTURES - ALOGD("Gestures: TAP finished"); -#endif - *outFinishPreviousGesture = true; - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - return true; - } - } - - // We did not handle this timeout. - return false; - } - - const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); - const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); - - // Update the velocity tracker. - { - VelocityTracker::Position positions[MAX_POINTERS]; - uint32_t count = 0; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - positions[count].x = pointer.x * mPointerXMovementScale; - positions[count].y = pointer.y * mPointerYMovementScale; - } - mPointerGesture.velocityTracker.addMovement(when, - mCurrentCookedState.fingerIdBits, positions); - } - - // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning - // to NEUTRAL, then we should not generate tap event. - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER - && mPointerGesture.lastGestureMode != PointerGesture::TAP - && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { - mPointerGesture.resetTap(); - } - - // Pick a new active touch id if needed. - // Choose an arbitrary pointer that just went down, if there is one. - // Otherwise choose an arbitrary remaining pointer. - // This guarantees we always have an active touch id when there is at least one pointer. - // We keep the same active touch id for as long as possible. - int32_t lastActiveTouchId = mPointerGesture.activeTouchId; - int32_t activeTouchId = lastActiveTouchId; - if (activeTouchId < 0) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - mPointerGesture.firstTouchTime = when; - } - } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = false; - if (activeTouchId < 0) { - mPointerGesture.resetQuietTime(); - } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS - || mPointerGesture.lastGestureMode == PointerGesture::SWIPE - || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) - && currentFingerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG - && currentFingerCount >= 2 - && !isPointerDown(mCurrentRawState.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; - } - } - } - - // Switch states based on button and pointer state. - if (isQuietTime) { - // Case 1: Quiet time. (QUIET) -#if DEBUG_GESTURES - ALOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime - + mConfig.pointerGestureQuietInterval - when) * 0.000001f); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { - *outFinishPreviousGesture = true; - } - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::QUIET; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - } else if (isPointerDown(mCurrentRawState.buttonState)) { - // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) - // The pointer follows the active touch point. - // Emit DOWN, MOVE, UP events at the pointer location. - // - // Only the active touch matters; other fingers are ignored. This policy helps - // to handle the case where the user places a second finger on the touch pad - // to apply the necessary force to depress an integrated button below the surface. - // We don't want the second finger to be delivered to applications. - // - // For this to work well, we need to make sure to track the pointer that is really - // active. If the user first puts one finger down to click then adds another - // finger to drag then the active pointer should switch to the finger that is - // being dragged. -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " - "currentFingerCount=%d", activeTouchId, currentFingerCount); -#endif - // Reset state when just starting. - if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { - *outFinishPreviousGesture = true; - mPointerGesture.activeGestureId = 0; - } - - // Switch pointers if needed. - // Find the fastest pointer and follow it. - if (activeTouchId >= 0 && currentFingerCount > 1) { - int32_t bestId = -1; - float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); - if (speed > bestSpeed) { - bestId = id; - bestSpeed = speed; - } - } - } - if (bestId >= 0 && bestId != activeTouchId) { - mPointerGesture.activeTouchId = activeTouchId = bestId; -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " - "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed); -#endif - } - } - - float deltaX = 0, deltaY = 0; - if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the click will occur at the position of the anchor - // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (currentFingerCount == 0) { - // Case 3. No fingers down and button is not pressed. (NEUTRAL) - if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { - *outFinishPreviousGesture = true; - } - - // Watch for taps coming out of HOVER or TAP_DRAG mode. - // Checking for taps after TAP_DRAG allows us to detect double-taps. - bool tapped = false; - if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER - || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) - && lastFingerCount == 1) { - if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP"); -#endif - - mPointerGesture.tapUpTime = when; - getContext()->requestTimeoutAtTime(when - + mConfig.pointerGestureTapDragInterval); - - mPointerGesture.activeGestureId = 0; - mPointerGesture.currentGestureMode = PointerGesture::TAP; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit( - mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[ - mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = - mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_X, mPointerGesture.tapX); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - tapped = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - if (mPointerGesture.tapDownTime != LLONG_MIN) { - ALOGD("Gestures: Not a TAP, %0.3fms since down", - (when - mPointerGesture.tapDownTime) * 0.000001f); - } else { - ALOGD("Gestures: Not a TAP, incompatible mode transitions"); - } -#endif - } - } - - mPointerVelocityControl.reset(); - - if (!tapped) { -#if DEBUG_GESTURES - ALOGD("Gestures: NEUTRAL"); -#endif - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - } - } else if (currentFingerCount == 1) { - // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) - // The pointer follows the active touch point. - // When in HOVER, emit HOVER_MOVE events at the pointer location. - // When in TAP_DRAG, emit MOVE events at the pointer location. - ALOG_ASSERT(activeTouchId >= 0); - - mPointerGesture.currentGestureMode = PointerGesture::HOVER; - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", - (when - mPointerGesture.tapUpTime) * 0.000001f); -#endif - } - } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } - - float deltaX = 0, deltaY = 0; - if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - bool down; - if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP_DRAG"); -#endif - down = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: HOVER"); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { - *outFinishPreviousGesture = true; - } - mPointerGesture.activeGestureId = 0; - down = false; - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - down ? 1.0f : 0.0f); - - if (lastFingerCount == 0 && currentFingerCount != 0) { - mPointerGesture.resetTap(); - mPointerGesture.tapDownTime = when; - mPointerGesture.tapX = x; - mPointerGesture.tapY = y; - } - } else { - // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) - // We need to provide feedback for each finger that goes down so we cannot wait - // for the fingers to move before deciding what to do. - // - // The ambiguous case is deciding what to do when there are two fingers down but they - // have not moved enough to determine whether they are part of a drag or part of a - // freeform gesture, or just a press or long-press at the pointer location. - // - // When there are two fingers we start with the PRESS hypothesis and we generate a - // down at the pointer location. - // - // When the two fingers move enough or when additional fingers are added, we make - // a decision to transition into SWIPE or FREEFORM mode accordingly. - ALOG_ASSERT(activeTouchId >= 0); - - bool settled = when >= mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval; - if (mPointerGesture.lastGestureMode != PointerGesture::PRESS - && mPointerGesture.lastGestureMode != PointerGesture::SWIPE - && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - *outFinishPreviousGesture = true; - } else if (!settled && currentFingerCount > lastFingerCount) { - // Additional pointers have gone down but not yet settled. - // Reset the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval - when) - * 0.000001f); -#endif - *outCancelPreviousGesture = true; - } else { - // Continue previous gesture. - mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; - } - - if (*outFinishPreviousGesture || *outCancelPreviousGesture) { - mPointerGesture.currentGestureMode = PointerGesture::PRESS; - mPointerGesture.activeGestureId = 0; - mPointerGesture.referenceIdBits.clear(); - mPointerVelocityControl.reset(); - - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval - when) - * 0.000001f); -#endif - mCurrentRawState.rawPointerData.getCentroidOfTouchingPointers( - &mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } - - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value - & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; - - // Add delta for all fingers and calculate a common movement delta. - float commonDeltaX = 0, commonDeltaY = 0; - BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value - & mCurrentCookedState.fingerIdBits.value); - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - bool first = (idBits == commonIdBits); - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); - const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } - } - - // Consider transitions from PRESS to SWIPE or MULTITOUCH. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float dist[MAX_POINTER_ID + 1]; - int32_t distOverThreshold = 0; - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - dist[id] = hypotf(delta.dx * mPointerXZoomScale, - delta.dy * mPointerYZoomScale); - if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { - distOverThreshold += 1; - } - } - - // Only transition when at least two pointers have moved further than - // the minimum distance threshold. - if (distOverThreshold >= 2) { - if (currentFingerCount > 2) { - // There are more than two pointers, switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are exactly two pointers. - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - uint32_t id1 = idBits.clearFirstMarkedBit(); - uint32_t id2 = idBits.firstMarkedBit(); - const RawPointerData::Pointer& p1 = - mCurrentRawState.rawPointerData.pointerForId(id1); - const RawPointerData::Pointer& p2 = - mCurrentRawState.rawPointerData.pointerForId(id2); - float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); - if (mutualDistance > mPointerGestureMaxSwipeWidth) { - // There are two pointers but they are too far apart for a SWIPE, - // switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - mutualDistance, mPointerGestureMaxSwipeWidth); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - float dist1 = dist[id1]; - float dist2 = dist[id2]; - if (dist1 >= mConfig.pointerGestureMultitouchMinDistance - && dist2 >= mConfig.pointerGestureMultitouchMinDistance) { - // Calculate the dot product of the displacement vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). - PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; - PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; - float dx1 = delta1.dx * mPointerXZoomScale; - float dy1 = delta1.dy * mPointerYZoomScale; - float dx2 = delta2.dx * mPointerXZoomScale; - float dy2 = delta2.dy * mPointerYZoomScale; - float dot = dx1 * dx2 + dy1 * dy2; - float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to SWIPE, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, - dist2, mConfig.pointerGestureMultitouchMinDistance, - cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, - dist2, mConfig.pointerGestureMultitouchMinDistance, - cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - } - } - } - } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // Switch from SWIPE to FREEFORM if additional pointers go down. - // Cancel previous gesture. - if (currentFingerCount > 2) { -#if DEBUG_GESTURES - ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - - // Move the reference points based on the overall group motion of the fingers - // except in PRESS mode while waiting for a transition to occur. - if (mPointerGesture.currentGestureMode != PointerGesture::PRESS - && (commonDeltaX || commonDeltaY)) { - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; - - commonDeltaX *= mPointerXMovementScale; - commonDeltaY *= mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); - mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - } - - // Report gestures. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS - || mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // PRESS or SWIPE mode. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - // FREEFORM mode. -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - - BitSet32 mappedTouchIdBits; - BitSet32 usedGestureIdBits; - if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - // Initially, assign the active gesture id to the active touch point - // if there is one. No other touch id bits are mapped yet. - if (!*outCancelPreviousGesture) { - mappedTouchIdBits.markBit(activeTouchId); - usedGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = - mPointerGesture.activeGestureId; - } else { - mPointerGesture.activeGestureId = -1; - } - } else { - // Otherwise, assume we mapped all touches from the previous frame. - // Reuse all mappings that are still applicable. - mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value - & mCurrentCookedState.fingerIdBits.value; - usedGestureIdBits = mPointerGesture.lastGestureIdBits; - - // Check whether we need to choose a new active gesture id because the - // current went went up. - for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value - & ~mCurrentCookedState.fingerIdBits.value); - !upTouchIdBits.isEmpty(); ) { - uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); - uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; - if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { - mPointerGesture.activeGestureId = -1; - break; - } - } - } - -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM follow up " - "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " - "activeGestureId=%d", - mappedTouchIdBits.value, usedGestureIdBits.value, - mPointerGesture.activeGestureId); -#endif - - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - for (uint32_t i = 0; i < currentFingerCount; i++) { - uint32_t touchId = idBits.clearFirstMarkedBit(); - uint32_t gestureId; - if (!mappedTouchIdBits.hasBit(touchId)) { - gestureId = usedGestureIdBits.markFirstUnmarkedBit(); - mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "new mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } else { - gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "existing mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } - mPointerGesture.currentGestureIdBits.markBit(gestureId); - mPointerGesture.currentGestureIdToIndex[gestureId] = i; - - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(touchId); - float deltaX = (pointer.x - mPointerGesture.referenceTouchX) - * mPointerXZoomScale; - float deltaY = (pointer.y - mPointerGesture.referenceTouchY) - * mPointerYZoomScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - - mPointerGesture.currentGestureProperties[i].clear(); - mPointerGesture.currentGestureProperties[i].id = gestureId; - mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[i].clear(); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } - - if (mPointerGesture.activeGestureId < 0) { - mPointerGesture.activeGestureId = - mPointerGesture.currentGestureIdBits.firstMarkedBit(); -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM new " - "activeGestureId=%d", mPointerGesture.activeGestureId); -#endif - } - } - } - - mPointerController->setButtonState(mCurrentRawState.buttonState); - -#if DEBUG_GESTURES - ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGestureIdBits=0x%08x", - toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); - for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; - ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, - coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } - for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; - ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, - coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } -#endif - return true; -} - -void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); - uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); - float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); - mPointerController->setPosition(x, y); - - hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); - down = !hovering; - - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[index]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; - } else { - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); -} - -void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); - uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - float deltaX = 0, deltaY = 0; - if (mLastCookedState.mouseIdBits.hasBit(id)) { - uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - - mLastRawState.rawPointerData.pointers[lastIndex].x) - * mPointerXMovementScale; - deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - - mLastRawState.rawPointerData.pointers[lastIndex].y) - * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - down = isPointerDown(mCurrentRawState.buttonState); - hovering = !down; - - float x, y; - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - hovering ? 0.0f : 1.0f); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; - } else { - mPointerVelocityControl.reset(); - - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); - - mPointerVelocityControl.reset(); -} - -void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, - bool down, bool hovering) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t displayId = mViewport.displayId; - - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); - - float xCursorPosition; - float yCursorPosition; - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - - if (mPointerSimple.down && !down) { - mPointerSimple.down = false; - - // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mPointerSimple.hovering && !hovering) { - mPointerSimple.hovering = false; - - // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (down) { - if (!mPointerSimple.down) { - mPointerSimple.down = true; - mPointerSimple.downTime = when; - - // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (hovering) { - if (!mPointerSimple.hovering) { - mPointerSimple.hovering = true; - - // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { - float vscroll = mCurrentRawState.rawVScroll; - float hscroll = mCurrentRawState.rawHScroll; - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - // Send scroll. - PointerCoords pointerCoords; - pointerCoords.copyFrom(mPointerSimple.currentCoords); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Save state. - if (down || hovering) { - mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); - mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); - } else { - mPointerSimple.reset(); - } -} - -void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - dispatchPointerSimple(when, policyFlags, false, false); -} - -void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, int32_t edgeFlags, - const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, - BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime) { - PointerCoords pointerCoords[MAX_POINTERS]; - PointerProperties pointerProperties[MAX_POINTERS]; - uint32_t pointerCount = 0; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = idToIndex[id]; - pointerProperties[pointerCount].copyFrom(properties[index]); - pointerCoords[pointerCount].copyFrom(coords[index]); - - if (changedId >= 0 && id == uint32_t(changedId)) { - action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - - pointerCount += 1; - } - - ALOG_ASSERT(pointerCount != 0); - - if (changedId >= 0 && pointerCount == 1) { - // Replace initial down and final up action. - // We can compare the action without masking off the changed pointer index - // because we know the index is 0. - if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { - action = AMOTION_EVENT_ACTION_DOWN; - } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { - action = AMOTION_EVENT_ACTION_UP; - } else { - // Can't happen. - ALOG_ASSERT(false); - } - } - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mDeviceMode == DEVICE_MODE_POINTER) { - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - } - const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); - const int32_t deviceId = getDeviceId(); - std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); - std::for_each(frames.begin(), frames.end(), - [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, - pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, - downTime, std::move(frames)); - getListener()->notifyMotion(&args); -} - -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, const uint32_t* inIdToIndex, - PointerProperties* outProperties, PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; -} - -void TouchInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -void TouchInputMapper::cancelTouch(nsecs_t when) { - abortPointerUsage(when, 0 /*policyFlags*/); - abortTouches(when, 0 /* policyFlags*/); -} - -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - const float scaledX = x * mXScale; - const float scaledY = y * mYScale; - return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue - && scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth - && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue - && scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; -} - -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { - - for (const VirtualKey& virtualKey: mVirtualKeys) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return & virtualKey; - } - } - - return nullptr; -} - -void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { - uint32_t currentPointerCount = current->rawPointerData.pointerCount; - uint32_t lastPointerCount = last->rawPointerData.pointerCount; - - current->rawPointerData.clearIdBits(); - - if (currentPointerCount == 0) { - // No pointers to assign. - return; - } - - if (lastPointerCount == 0) { - // All pointers are new. - for (uint32_t i = 0; i < currentPointerCount; i++) { - uint32_t id = i; - current->rawPointerData.pointers[i].id = id; - current->rawPointerData.idToIndex[id] = i; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); - } - return; - } - - if (currentPointerCount == 1 && lastPointerCount == 1 - && current->rawPointerData.pointers[0].toolType - == last->rawPointerData.pointers[0].toolType) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = last->rawPointerData.pointers[0].id; - current->rawPointerData.pointers[0].id = id; - current->rawPointerData.idToIndex[id] = 0; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); - return; - } - - // General case. - // 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. - // The pointers must have the same tool type but it is possible for them to - // transition from hovering to touching or vice-versa while retaining the same id. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - const RawPointerData::Pointer& currentPointer = - current->rawPointerData.pointers[currentPointerIndex]; - const RawPointerData::Pointer& lastPointer = - last->rawPointerData.pointers[lastPointerIndex]; - if (currentPointer.toolType == lastPointer.toolType) { - int64_t deltaX = currentPointer.x - lastPointer.x; - int64_t deltaY = currentPointer.y - lastPointer.y; - - 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; - } - } - } - - // 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; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, - 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. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { - while (heapSize > 0) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - } - - heapSize -= 1; - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 - ", id=%" PRIu32 ", distance=%" PRIu64, - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to pointers that were not matched in the process. - for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { - uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); - uint32_t id = usedIdBits.markFirstUnmarkedBit(); - - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); -#endif - } -} - -int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.keyCode == keyCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.scanCode == scanCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - for (const VirtualKey& virtualKey : mVirtualKeys) { - for (size_t i = 0; i < numCodes; i++) { - if (virtualKey.keyCode == keyCodes[i]) { - outFlags[i] = 1; - } - } - } - - return true; -} - -std::optional TouchInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - return std::make_optional(mViewport.displayId); - } - } - return std::nullopt; -} - -// --- SingleTouchInputMapper --- - -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : - TouchInputMapper(device) { -} - -SingleTouchInputMapper::~SingleTouchInputMapper() { -} - -void SingleTouchInputMapper::reset(nsecs_t when) { - mSingleTouchMotionAccumulator.reset(getDevice()); - - TouchInputMapper::reset(when); -} - -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mSingleTouchMotionAccumulator.process(rawEvent); -} - -void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - if (mTouchButtonAccumulator.isToolActive()) { - outState->rawPointerData.pointerCount = 1; - outState->rawPointerData.idToIndex[0] = 0; - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE - && (mTouchButtonAccumulator.isHovering() - || (mRawPointerAxes.pressure.valid - && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); - outState->rawPointerData.markIdBit(0, isHovering); - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; - outPointer.id = 0; - outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); - outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); - outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - outPointer.touchMajor = 0; - outPointer.touchMinor = 0; - outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.orientation = 0; - outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); - outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); - outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - outPointer.isHovering = isHovering; - } -} - -void SingleTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); - getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); -} - -bool SingleTouchInputMapper::hasStylus() const { - return mTouchButtonAccumulator.hasStylus(); -} - - -// --- MultiTouchInputMapper --- - -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : - TouchInputMapper(device) { -} - -MultiTouchInputMapper::~MultiTouchInputMapper() { -} - -void MultiTouchInputMapper::reset(nsecs_t when) { - mMultiTouchMotionAccumulator.reset(getDevice()); - - mPointerIdBits.clear(); - - TouchInputMapper::reset(when); -} - -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mMultiTouchMotionAccumulator.process(rawEvent); -} - -void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); - size_t outCount = 0; - BitSet32 newPointerIdBits; - mHavePointerIds = true; - - for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const MultiTouchMotionAccumulator::Slot* inSlot = - mMultiTouchMotionAccumulator.getSlot(inIndex); - if (!inSlot->isInUse()) { - continue; - } - - if (outCount >= MAX_POINTERS) { -#if DEBUG_POINTERS - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); -#endif - break; // too many fingers! - } - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; - outPointer.x = inSlot->getX(); - outPointer.y = inSlot->getY(); - outPointer.pressure = inSlot->getPressure(); - outPointer.touchMajor = inSlot->getTouchMajor(); - outPointer.touchMinor = inSlot->getTouchMinor(); - outPointer.toolMajor = inSlot->getToolMajor(); - outPointer.toolMinor = inSlot->getToolMinor(); - outPointer.orientation = inSlot->getOrientation(); - outPointer.distance = inSlot->getDistance(); - outPointer.tiltX = 0; - outPointer.tiltY = 0; - - outPointer.toolType = inSlot->getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - } - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE - && (mTouchButtonAccumulator.isHovering() - || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); - outPointer.isHovering = isHovering; - - // Assign pointer id using tracking id if available. - if (mHavePointerIds) { - int32_t trackingId = inSlot->getTrackingId(); - int32_t id = -1; - if (trackingId >= 0) { - for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { - uint32_t n = idBits.clearFirstMarkedBit(); - if (mPointerTrackingIdMap[n] == trackingId) { - id = n; - } - } - - if (id < 0 && !mPointerIdBits.isFull()) { - id = mPointerIdBits.markFirstUnmarkedBit(); - mPointerTrackingIdMap[id] = trackingId; - } - } - if (id < 0) { - mHavePointerIds = false; - outState->rawPointerData.clearIdBits(); - newPointerIdBits.clear(); - } else { - outPointer.id = id; - outState->rawPointerData.idToIndex[id] = outCount; - outState->rawPointerData.markIdBit(id, isHovering); - newPointerIdBits.markBit(id); - } - } - outCount += 1; - } - - outState->rawPointerData.pointerCount = outCount; - mPointerIdBits = newPointerIdBits; - - mMultiTouchMotionAccumulator.finishSync(); -} - -void MultiTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); - getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); - getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); - getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); - - if (mRawPointerAxes.trackingId.valid - && mRawPointerAxes.slot.valid - && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { - size_t slotCount = mRawPointerAxes.slot.maxValue + 1; - if (slotCount > MAX_SLOTS) { - ALOGW("MultiTouch Device %s reported %zu slots but the framework " - "only supports a maximum of %zu slots at this time.", - getDeviceName().c_str(), slotCount, MAX_SLOTS); - slotCount = MAX_SLOTS; - } - mMultiTouchMotionAccumulator.configure(getDevice(), - slotCount, true /*usingSlotsProtocol*/); - } else { - mMultiTouchMotionAccumulator.configure(getDevice(), - MAX_POINTERS, false /*usingSlotsProtocol*/); - } -} - -bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() - || mTouchButtonAccumulator.hasStylus(); -} - -// --- ExternalStylusInputMapper - -ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : - InputMapper(device) { - -} - -uint32_t ExternalStylusInputMapper::getSources() { - return AINPUT_SOURCE_STYLUS; -} - -void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, - 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); -} - -void ExternalStylusInputMapper::dump(std::string& dump) { - dump += INDENT2 "External Stylus Input Mapper:\n"; - dump += INDENT3 "Raw Stylus Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); - dump += INDENT3 "Stylus State:\n"; - dumpStylusState(dump, mStylusState); -} - -void ExternalStylusInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDevice()); -} - -void ExternalStylusInputMapper::reset(nsecs_t when) { - InputDevice* device = getDevice(); - mSingleTouchMotionAccumulator.reset(device); - mTouchButtonAccumulator.reset(device); - InputMapper::reset(when); -} - -void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { - mSingleTouchMotionAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void ExternalStylusInputMapper::sync(nsecs_t when) { - mStylusState.clear(); - - mStylusState.when = when; - - mStylusState.toolType = mTouchButtonAccumulator.getToolType(); - if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; - } - - mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - - mContext->dispatchExternalStylusState(mStylusState); -} - - -// --- JoystickInputMapper --- - -JoystickInputMapper::JoystickInputMapper(InputDevice* device) : - InputMapper(device) { -} - -JoystickInputMapper::~JoystickInputMapper() { -} - -uint32_t JoystickInputMapper::getSources() { - return AINPUT_SOURCE_JOYSTICK; -} - -void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - for (size_t i = 0; i < mAxes.size(); i++) { - const Axis& axis = mAxes.valueAt(i); - addMotionRange(axis.axisInfo.axis, axis, info); - - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - addMotionRange(axis.axisInfo.highAxis, axis, info); - - } - } -} - -void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, - InputDeviceInfo* info) { - info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to register - * the old axes as duplicates of their corresponding new ones. */ - int32_t compatAxis = getCompatAxis(axisId); - if (compatAxis >= 0) { - info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - } -} - -/* A mapping from axes the joystick actually has to the axes that should be - * artificially created for compatibility purposes. - * Returns -1 if no compatibility axis is needed. */ -int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { - switch(axis) { - case AMOTION_EVENT_AXIS_LTRIGGER: - return AMOTION_EVENT_AXIS_BRAKE; - case AMOTION_EVENT_AXIS_RTRIGGER: - return AMOTION_EVENT_AXIS_GAS; - } - return -1; -} - -void JoystickInputMapper::dump(std::string& dump) { - dump += INDENT2 "Joystick Input Mapper:\n"; - - dump += INDENT3 "Axes:\n"; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - const char* label = getAxisLabel(axis.axisInfo.axis); - if (label) { - dump += StringPrintf(INDENT4 "%s", label); - } else { - dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - label = getAxisLabel(axis.axisInfo.highAxis); - if (label) { - dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); - } else { - dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, - axis.axisInfo.splitValue); - } - } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { - dump += " (invert)"; - } - - dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " - "highScale=%0.5f, highOffset=%0.5f\n", - axis.scale, axis.offset, axis.highScale, axis.highOffset); - dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " - "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", - mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, - axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution); - } -} - -void JoystickInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Collect all axes. - for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) - & INPUT_DEVICE_CLASS_JOYSTICK)) { - continue; // axis must be claimed by a different device - } - - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { - // Map axis. - AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); - if (!explicitlyMapped) { - // Axis is not explicitly mapped, will choose a generic axis later. - axisInfo.mode = AxisInfo::MODE_NORMAL; - axisInfo.axis = -1; - } - - // Apply flat override. - int32_t rawFlat = axisInfo.flatOverride < 0 - ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, highScale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, offset, scale, offset, - -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, scale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } - - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; - - mAxes.add(abs, axis); - } - } - - // If there are too many axes, start dropping them. - // Prefer to keep explicitly mapped axes. - if (mAxes.size() > PointerCoords::MAX_AXES) { - ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", - getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); - pruneAxes(true); - pruneAxes(false); - } - - // Assign generic axis ids to remaining axes. - int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 - && haveAxis(nextGenericAxisId)) { - nextGenericAxisId += 1; - } - - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { - axis.axisInfo.axis = nextGenericAxisId; - nextGenericAxisId += 1; - } else { - ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " - "have already been assigned to other axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; - } - } - } - } -} - -bool JoystickInputMapper::haveAxis(int32_t axisId) { - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - if (axis.axisInfo.axis == axisId - || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT - && axis.axisInfo.highAxis == axisId)) { - return true; - } - } - return false; -} - -void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { - size_t i = mAxes.size(); - while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { - if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { - continue; - } - ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i); - } -} - -bool JoystickInputMapper::isCenteredAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_X: - case AMOTION_EVENT_AXIS_Y: - case AMOTION_EVENT_AXIS_Z: - case AMOTION_EVENT_AXIS_RX: - case AMOTION_EVENT_AXIS_RY: - case AMOTION_EVENT_AXIS_RZ: - case AMOTION_EVENT_AXIS_HAT_X: - case AMOTION_EVENT_AXIS_HAT_Y: - case AMOTION_EVENT_AXIS_ORIENTATION: - case AMOTION_EVENT_AXIS_RUDDER: - case AMOTION_EVENT_AXIS_WHEEL: - return true; - default: - return false; - } -} - -void JoystickInputMapper::reset(nsecs_t when) { - // Recenter all axes. - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - axis.resetValue(); - } - - InputMapper::reset(when); -} - -void JoystickInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->code); - if (index >= 0) { - Axis& axis = mAxes.editValueAt(index); - float newValue, highNewValue; - switch (axis.axisInfo.mode) { - case AxisInfo::MODE_INVERT: - newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) - * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - case AxisInfo::MODE_SPLIT: - if (rawEvent->value < axis.axisInfo.splitValue) { - newValue = (axis.axisInfo.splitValue - rawEvent->value) - * axis.scale + axis.offset; - highNewValue = 0.0f; - } else if (rawEvent->value > axis.axisInfo.splitValue) { - newValue = 0.0f; - highNewValue = (rawEvent->value - axis.axisInfo.splitValue) - * axis.highScale + axis.highOffset; - } else { - newValue = 0.0f; - highNewValue = 0.0f; - } - break; - default: - newValue = rawEvent->value * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - } - axis.newValue = newValue; - axis.highNewValue = highNewValue; - } - break; - } - - case EV_SYN: - switch (rawEvent->code) { - case SYN_REPORT: - sync(rawEvent->when, false /*force*/); - break; - } - break; - } -} - -void JoystickInputMapper::sync(nsecs_t when, bool force) { - if (!filterAxes(force)) { - return; - } - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = 0; - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, - axis.highCurrentValue); - } - } - - // Moving a joystick axis should not wake the device because joysticks can - // be fairly noisy even when not in use. On the other hand, pushing a gamepad - // button will likely wake the device. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); -} - -void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, - int32_t axis, float value) { - pointerCoords->setAxisValue(axis, value); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to produce - * values for the old axes as mirrors of the value of their corresponding - * new axes. */ - int32_t compatAxis = getCompatAxis(axis); - if (compatAxis >= 0) { - pointerCoords->setAxisValue(compatAxis, value); - } -} - -bool JoystickInputMapper::filterAxes(bool force) { - bool atLeastOneSignificantChange = force; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (force || hasValueChangedSignificantly(axis.filter, - axis.newValue, axis.currentValue, axis.min, axis.max)) { - axis.currentValue = axis.newValue; - atLeastOneSignificantChange = true; - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - if (force || hasValueChangedSignificantly(axis.filter, - axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) { - axis.highCurrentValue = axis.highNewValue; - atLeastOneSignificantChange = true; - } - } - } - return atLeastOneSignificantChange; -} - -bool JoystickInputMapper::hasValueChangedSignificantly( - float filter, float newValue, float currentValue, float min, float max) { - if (newValue != currentValue) { - // Filter out small changes in value unless the value is converging on the axis - // bounds or center point. This is intended to reduce the amount of information - // sent to applications by particularly noisy joysticks (such as PS3). - if (fabs(newValue - currentValue) > filter - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { - return true; - } - } - return false; -} - -bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange( - float filter, float newValue, float currentValue, float thresholdValue) { - float newDistance = fabs(newValue - thresholdValue); - if (newDistance < filter) { - float oldDistance = fabs(currentValue - thresholdValue); - if (newDistance < oldDistance) { - return true; - } - } - return false; -} - -} // namespace android diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h deleted file mode 100644 index 0666ca54bf..0000000000 --- a/services/inputflinger/InputReader.h +++ /dev/null @@ -1,1743 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_READER_H -#define _UI_INPUT_READER_H - -#include "EventHub.h" -#include "PointerControllerInterface.h" -#include "InputListener.h" -#include "InputReaderBase.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { - -class InputDevice; -class InputMapper; - - -struct StylusState { - /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; - /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; - /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; - - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } -}; - - -/* Internal interface used by individual input devices to access global input device state - * and parameters maintained by the input reader. - */ -class InputReaderContext { -public: - InputReaderContext() { } - virtual ~InputReaderContext() { } - - virtual void updateGlobalMetaState() = 0; - virtual int32_t getGlobalMetaState() = 0; - - virtual void disableVirtualKeysUntil(nsecs_t time) = 0; - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) = 0; - - virtual void fadePointer() = 0; - - virtual void requestTimeoutAtTime(nsecs_t when) = 0; - virtual int32_t bumpGeneration() = 0; - - virtual void getExternalStylusDevices(std::vector& outDevices) = 0; - virtual void dispatchExternalStylusState(const StylusState& outState) = 0; - - virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; - virtual EventHubInterface* getEventHub() = 0; - - virtual uint32_t getNextSequenceNum() = 0; -}; - - -/* The input reader reads raw event data from the event hub and processes it into input events - * that it sends to the input listener. Some functions of the input reader, such as early - * event filtering in low power states, are controlled by a separate policy object. - * - * The InputReader owns a collection of InputMappers. Most of the work it does happens - * on the input reader thread but the InputReader can receive queries from other system - * components running on arbitrary threads. To keep things manageable, the InputReader - * uses a single Mutex to guard its state. The Mutex may be held while calling into the - * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. - */ -class InputReader : public InputReaderInterface { -public: - InputReader(std::shared_ptr eventHub, - const sp& policy, - const sp& listener); - virtual ~InputReader(); - - virtual void dump(std::string& dump); - virtual void monitor(); - - virtual void loopOnce(); - - virtual void getInputDevices(std::vector& outInputDevices); - - virtual bool isInputDeviceEnabled(int32_t deviceId); - - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode); - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode); - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, - int32_t sw); - - virtual void toggleCapsLockState(int32_t deviceId); - - virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); - - virtual void requestRefreshConfiguration(uint32_t changes); - - virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t deviceId, int32_t token); - - virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); -protected: - // These members are protected so they can be instrumented by test cases. - virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, uint32_t classes); - - class ContextImpl : public InputReaderContext { - InputReader* mReader; - - public: - explicit ContextImpl(InputReader* reader); - - virtual void updateGlobalMetaState(); - virtual int32_t getGlobalMetaState(); - virtual void disableVirtualKeysUntil(nsecs_t time); - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode); - virtual void fadePointer(); - virtual void requestTimeoutAtTime(nsecs_t when); - virtual int32_t bumpGeneration(); - virtual void getExternalStylusDevices(std::vector& outDevices); - virtual void dispatchExternalStylusState(const StylusState& outState); - virtual InputReaderPolicyInterface* getPolicy(); - virtual InputListenerInterface* getListener(); - virtual EventHubInterface* getEventHub(); - virtual uint32_t getNextSequenceNum(); - } mContext; - - friend class ContextImpl; - -private: - Mutex mLock; - - Condition mReaderIsAliveCondition; - - // This could be unique_ptr, but due to the way InputReader tests are written, - // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test - // in parallel to passing it to the InputReader. - std::shared_ptr mEventHub; - sp mPolicy; - sp mQueuedListener; - - InputReaderConfiguration mConfig; - - // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers - uint32_t mNextSequenceNum; - - // The event queue. - static const int EVENT_BUFFER_SIZE = 256; - RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - - KeyedVector mDevices; - - // low-level input event decoding and device management - void processEventsLocked(const RawEvent* rawEvents, size_t count); - - void addDeviceLocked(nsecs_t when, int32_t deviceId); - void removeDeviceLocked(nsecs_t when, int32_t deviceId); - void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); - void timeoutExpiredLocked(nsecs_t when); - - void handleConfigurationChangedLocked(nsecs_t when); - - int32_t mGlobalMetaState; - void updateGlobalMetaStateLocked(); - int32_t getGlobalMetaStateLocked(); - - void notifyExternalStylusPresenceChanged(); - void getExternalStylusDevicesLocked(std::vector& outDevices); - void dispatchExternalStylusState(const StylusState& state); - - void fadePointerLocked(); - - int32_t mGeneration; - int32_t bumpGenerationLocked(); - - void getInputDevicesLocked(std::vector& outInputDevices); - - nsecs_t mDisableVirtualKeysTimeout; - void disableVirtualKeysUntilLocked(nsecs_t time); - bool shouldDropVirtualKeyLocked(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode); - - nsecs_t mNextTimeout; - void requestTimeoutAtTimeLocked(nsecs_t when); - - uint32_t mConfigurationChangesToRefresh; - void refreshConfigurationLocked(uint32_t changes); - - // state queries - typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); - int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, - GetStateFunc getStateFunc); - bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); -}; - - -/* Represents the state of a single input device. */ -class InputDevice { -public: - InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t - controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes); - ~InputDevice(); - - inline InputReaderContext* getContext() { return mContext; } - inline int32_t getId() const { return mId; } - inline int32_t getControllerNumber() const { return mControllerNumber; } - inline int32_t getGeneration() const { return mGeneration; } - inline const std::string getName() const { return mIdentifier.name; } - inline const std::string getDescriptor() { return mIdentifier.descriptor; } - inline uint32_t getClasses() const { return mClasses; } - inline uint32_t getSources() const { return mSources; } - - inline bool isExternal() { return mIsExternal; } - inline void setExternal(bool external) { mIsExternal = external; } - inline std::optional getAssociatedDisplayPort() const { - return mAssociatedDisplayPort; - } - inline std::optional getAssociatedViewport() const { - return mAssociatedViewport; - } - inline void setMic(bool hasMic) { mHasMic = hasMic; } - inline bool hasMic() const { return mHasMic; } - - inline bool isIgnored() { return mMappers.empty(); } - - bool isEnabled(); - void setEnabled(bool enabled, nsecs_t when); - - void dump(std::string& dump); - void addMapper(InputMapper* mapper); - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - void reset(nsecs_t when); - void process(const RawEvent* rawEvents, size_t count); - void timeoutExpired(nsecs_t when); - void updateExternalStylusState(const StylusState& state); - - void getDeviceInfo(InputDeviceInfo* outDeviceInfo); - int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - void cancelVibrate(int32_t token); - void cancelTouch(nsecs_t when); - - int32_t getMetaState(); - void updateMetaState(int32_t keyCode); - - void fadePointer(); - - void bumpGeneration(); - - void notifyReset(nsecs_t when); - - inline const PropertyMap& getConfiguration() { return mConfiguration; } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - bool hasKey(int32_t code) { - return getEventHub()->hasScanCode(mId, code); - } - - bool hasAbsoluteAxis(int32_t code) { - RawAbsoluteAxisInfo info; - getEventHub()->getAbsoluteAxisInfo(mId, code, &info); - return info.valid; - } - - bool isKeyPressed(int32_t code) { - return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; - } - - int32_t getAbsoluteAxisValue(int32_t code) { - int32_t value; - getEventHub()->getAbsoluteAxisValue(mId, code, &value); - return value; - } - - std::optional getAssociatedDisplayId(); - -private: - InputReaderContext* mContext; - int32_t mId; - int32_t mGeneration; - int32_t mControllerNumber; - InputDeviceIdentifier mIdentifier; - std::string mAlias; - uint32_t mClasses; - - std::vector mMappers; - - uint32_t mSources; - bool mIsExternal; - std::optional mAssociatedDisplayPort; - std::optional mAssociatedViewport; - bool mHasMic; - bool mDropUntilNextSync; - - typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); - int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); - - PropertyMap mConfiguration; -}; - - -/* Keeps track of the state of mouse or touch pad buttons. */ -class CursorButtonAccumulator { -public: - CursorButtonAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - -private: - bool mBtnLeft; - bool mBtnRight; - bool mBtnMiddle; - bool mBtnBack; - bool mBtnSide; - bool mBtnForward; - bool mBtnExtra; - bool mBtnTask; - - void clearButtons(); -}; - - -/* Keeps track of cursor movements. */ - -class CursorMotionAccumulator { -public: - CursorMotionAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - -private: - int32_t mRelX; - int32_t mRelY; - - void clearRelativeAxes(); -}; - - -/* Keeps track of cursor scrolling motions. */ - -class CursorScrollAccumulator { -public: - CursorScrollAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline bool haveRelativeVWheel() const { return mHaveRelWheel; } - inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - inline int32_t getRelativeVWheel() const { return mRelWheel; } - inline int32_t getRelativeHWheel() const { return mRelHWheel; } - -private: - bool mHaveRelWheel; - bool mHaveRelHWheel; - - int32_t mRelX; - int32_t mRelY; - int32_t mRelWheel; - int32_t mRelHWheel; - - void clearRelativeAxes(); -}; - - -/* Keeps track of the state of touch, stylus and tool buttons. */ -class TouchButtonAccumulator { -public: - TouchButtonAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - int32_t getToolType() const; - bool isToolActive() const; - bool isHovering() const; - bool hasStylus() const; - -private: - bool mHaveBtnTouch; - bool mHaveStylus; - - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; - - void clearButtons(); -}; - - -/* Raw axis information from the driver. */ -struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); - inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } - inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); -}; - - -/* Raw data for a collection of pointers including a pointer id mapping table. */ -struct RawPointerData { - struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; - }; - - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); - void getCentroidOfTouchingPointers(float* outX, float* outY) const; - - inline void markIdBit(uint32_t id, bool isHovering) { - if (isHovering) { - hoveringIdBits.markBit(id); - } else { - touchingIdBits.markBit(id); - } - } - - inline void clearIdBits() { - hoveringIdBits.clear(); - touchingIdBits.clear(); - } - - inline const Pointer& pointerForId(uint32_t id) const { - return pointers[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) { - return pointers[pointerIndex].isHovering; - } -}; - - -/* Cooked data for a collection of pointers including a pointer id mapping table. */ -struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); - - inline const PointerCoords& pointerCoordsForId(uint32_t id) const { - return pointerCoords[idToIndex[id]]; - } - - inline PointerCoords& editPointerCoordsWithId(uint32_t id) { - return pointerCoords[idToIndex[id]]; - } - - inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { - return pointerProperties[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) const { - return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); - } - - inline bool isTouching(uint32_t pointerIndex) const { - return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); - } -}; - - -/* Keeps track of the state of single-touch protocol. */ -class SingleTouchMotionAccumulator { -public: - SingleTouchMotionAccumulator(); - - void process(const RawEvent* rawEvent); - void reset(InputDevice* device); - - inline int32_t getAbsoluteX() const { return mAbsX; } - inline int32_t getAbsoluteY() const { return mAbsY; } - inline int32_t getAbsolutePressure() const { return mAbsPressure; } - inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } - inline int32_t getAbsoluteDistance() const { return mAbsDistance; } - inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } - inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } - -private: - int32_t mAbsX; - int32_t mAbsY; - int32_t mAbsPressure; - int32_t mAbsToolWidth; - int32_t mAbsDistance; - int32_t mAbsTiltX; - int32_t mAbsTiltY; - - void clearAbsoluteAxes(); -}; - - -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse; - bool mHaveAbsMTTouchMinor; - bool mHaveAbsMTWidthMinor; - bool mHaveAbsMTToolType; - - int32_t mAbsMTPositionX; - int32_t mAbsMTPositionY; - int32_t mAbsMTTouchMajor; - int32_t mAbsMTTouchMinor; - int32_t mAbsMTWidthMajor; - int32_t mAbsMTWidthMinor; - int32_t mAbsMTOrientation; - int32_t mAbsMTTrackingId; - int32_t mAbsMTPressure; - int32_t mAbsMTDistance; - int32_t mAbsMTToolType; - - Slot(); - void clear(); - }; - - MultiTouchMotionAccumulator(); - ~MultiTouchMotionAccumulator(); - - void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); - void reset(InputDevice* device); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlotCount; } - inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - -private: - int32_t mCurrentSlot; - Slot* mSlots; - size_t mSlotCount; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void clearSlots(int32_t initialSlot); -}; - - -/* An input mapper transforms raw input events into cooked event data. - * A single input device can have multiple associated input mappers in order to interpret - * different classes of events. - * - * InputMapper lifecycle: - * - create - * - configure with 0 changes - * - reset - * - process, process, process (may occasionally reconfigure with non-zero changes or reset) - * - reset - * - destroy - */ -class InputMapper { -public: - explicit InputMapper(InputDevice* device); - virtual ~InputMapper(); - - inline InputDevice* getDevice() { return mDevice; } - inline int32_t getDeviceId() { return mDevice->getId(); } - inline const std::string getDeviceName() { return mDevice->getName(); } - inline InputReaderContext* getContext() { return mContext; } - inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline InputListenerInterface* getListener() { return mContext->getListener(); } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - virtual uint32_t getSources() = 0; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent) = 0; - virtual void timeoutExpired(nsecs_t when); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void cancelTouch(nsecs_t when); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - - virtual void updateExternalStylusState(const StylusState& state); - - virtual void fadePointer(); - virtual std::optional getAssociatedDisplayId() { return std::nullopt; } - -protected: - InputDevice* mDevice; - InputReaderContext* mContext; - - status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); - void bumpGeneration(); - - static void dumpRawAbsoluteAxisInfo(std::string& dump, - const RawAbsoluteAxisInfo& axis, const char* name); - static void dumpStylusState(std::string& dump, const StylusState& state); -}; - - -class SwitchInputMapper : public InputMapper { -public: - explicit SwitchInputMapper(InputDevice* device); - virtual ~SwitchInputMapper(); - - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); - -private: - uint32_t mSwitchValues; - uint32_t mUpdatedSwitchMask; - - void processSwitch(int32_t switchCode, int32_t switchValue); - void sync(nsecs_t when); -}; - - -class VibratorInputMapper : public InputMapper { -public: - explicit VibratorInputMapper(InputDevice* device); - virtual ~VibratorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); - -private: - bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; - ssize_t mRepeat; - int32_t mToken; - ssize_t mIndex; - nsecs_t mNextStepTime; - - void nextStep(); - void stopVibrating(); -}; - - -class KeyboardInputMapper : public InputMapper { -public: - KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - virtual std::optional getAssociatedDisplayId(); - -private: - // The current viewport. - std::optional mViewport; - - struct KeyDown { - int32_t keyCode; - int32_t scanCode; - }; - - uint32_t mSource; - int32_t mKeyboardType; - - std::vector mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down - - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none - - struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on - }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; - - // Immutable configuration parameters. - struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - } mParameters; - - void configureParameters(); - void dumpParameters(std::string& dump); - - int32_t getOrientation(); - int32_t getDisplayId(); - - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - - void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); - - bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - - ssize_t findKeyDown(int32_t scanCode); - - void resetLedState(); - void initializeLedState(LedState& ledState, int32_t led); - void updateLedState(bool reset); - void updateLedStateForModifier(LedState& ledState, int32_t led, - int32_t modifier, bool reset); - std::optional findViewport(nsecs_t when, - const InputReaderConfiguration* config); -}; - - -class CursorInputMapper : public InputMapper { -public: - explicit CursorInputMapper(InputDevice* device); - virtual ~CursorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - - virtual void fadePointer(); - - virtual std::optional getAssociatedDisplayId(); - -private: - // Amount that trackball needs to move in order to generate a key event. - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; - - // Immutable configuration parameters. - struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, - }; - - Mode mode; - bool hasAssociatedDisplay; - bool orientationAware; - } mParameters; - - CursorButtonAccumulator mCursorButtonAccumulator; - CursorMotionAccumulator mCursorMotionAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - - int32_t mSource; - float mXScale; - float mYScale; - float mXPrecision; - float mYPrecision; - - float mVWheelScale; - float mHWheelScale; - - // Velocity controls for mouse pointer and wheel movements. - // The controls for X and Y wheel movements are separate to keep them decoupled. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - int32_t mOrientation; - - sp mPointerController; - - int32_t mButtonState; - nsecs_t mDownTime; - - void configureParameters(); - void dumpParameters(std::string& dump); - - void sync(nsecs_t when); -}; - - -class RotaryEncoderInputMapper : public InputMapper { -public: - explicit RotaryEncoderInputMapper(InputDevice* device); - virtual ~RotaryEncoderInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - CursorScrollAccumulator mRotaryEncoderScrollAccumulator; - - int32_t mSource; - float mScalingFactor; - int32_t mOrientation; - - void sync(nsecs_t when); -}; - -class TouchInputMapper : public InputMapper { -public: - explicit TouchInputMapper(InputDevice* device); - virtual ~TouchInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional getAssociatedDisplayId(); - -protected: - CursorButtonAccumulator mCursorButtonAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - TouchButtonAccumulator mTouchButtonAccumulator; - - struct VirtualKey { - int32_t keyCode; - int32_t scanCode; - uint32_t flags; - - // computed hit box, specified in touch screen coords based on known display size - int32_t hitLeft; - int32_t hitTop; - int32_t hitRight; - int32_t hitBottom; - - inline bool isHit(int32_t x, int32_t y) const { - return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; - } - }; - - // Input sources and device mode. - uint32_t mSource; - - enum DeviceMode { - DEVICE_MODE_DISABLED, // input is disabled - DEVICE_MODE_DIRECT, // direct mapping (touchscreen) - DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) - DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - DEVICE_MODE_POINTER, // pointer mapping (pointer) - }; - DeviceMode mDeviceMode; - - // The reader's configuration. - InputReaderConfiguration mConfig; - - // Immutable configuration parameters. - struct Parameters { - enum DeviceType { - DEVICE_TYPE_TOUCH_SCREEN, - DEVICE_TYPE_TOUCH_PAD, - DEVICE_TYPE_TOUCH_NAVIGATION, - DEVICE_TYPE_POINTER, - }; - - DeviceType deviceType; - bool hasAssociatedDisplay; - bool associatedDisplayIsExternal; - bool orientationAware; - bool hasButtonUnderPad; - std::string uniqueDisplayId; - - enum GestureMode { - GESTURE_MODE_SINGLE_TOUCH, - GESTURE_MODE_MULTI_TOUCH, - }; - GestureMode gestureMode; - - bool wake; - } mParameters; - - // Immutable calibration parameters in parsed form. - struct Calibration { - // Size - enum SizeCalibration { - SIZE_CALIBRATION_DEFAULT, - SIZE_CALIBRATION_NONE, - SIZE_CALIBRATION_GEOMETRIC, - SIZE_CALIBRATION_DIAMETER, - SIZE_CALIBRATION_BOX, - SIZE_CALIBRATION_AREA, - }; - - SizeCalibration sizeCalibration; - - bool haveSizeScale; - float sizeScale; - bool haveSizeBias; - float sizeBias; - bool haveSizeIsSummed; - bool sizeIsSummed; - - // Pressure - enum PressureCalibration { - PRESSURE_CALIBRATION_DEFAULT, - PRESSURE_CALIBRATION_NONE, - PRESSURE_CALIBRATION_PHYSICAL, - PRESSURE_CALIBRATION_AMPLITUDE, - }; - - PressureCalibration pressureCalibration; - bool havePressureScale; - float pressureScale; - - // Orientation - enum OrientationCalibration { - ORIENTATION_CALIBRATION_DEFAULT, - ORIENTATION_CALIBRATION_NONE, - ORIENTATION_CALIBRATION_INTERPOLATED, - ORIENTATION_CALIBRATION_VECTOR, - }; - - OrientationCalibration orientationCalibration; - - // Distance - enum DistanceCalibration { - DISTANCE_CALIBRATION_DEFAULT, - DISTANCE_CALIBRATION_NONE, - DISTANCE_CALIBRATION_SCALED, - }; - - DistanceCalibration distanceCalibration; - bool haveDistanceScale; - float distanceScale; - - enum CoverageCalibration { - COVERAGE_CALIBRATION_DEFAULT, - COVERAGE_CALIBRATION_NONE, - COVERAGE_CALIBRATION_BOX, - }; - - CoverageCalibration coverageCalibration; - - inline void applySizeScaleAndBias(float* outSize) const { - if (haveSizeScale) { - *outSize *= sizeScale; - } - if (haveSizeBias) { - *outSize += sizeBias; - } - if (*outSize < 0) { - *outSize = 0; - } - } - } mCalibration; - - // Affine location transformation/calibration - struct TouchAffineTransformation mAffineTransform; - - RawPointerAxes mRawPointerAxes; - - struct RawState { - nsecs_t when; - - // Raw pointer sample data. - RawPointerData rawPointerData; - - int32_t buttonState; - - // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - void copyFrom(const RawState& other) { - when = other.when; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } - - void clear() { - when = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } - }; - - struct CookedState { - // Cooked pointer sample data. - CookedPointerData cookedPointerData; - - // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } - - void clear() { - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } - }; - - std::vector mRawStatesPending; - RawState mCurrentRawState; - CookedState mCurrentCookedState; - RawState mLastRawState; - CookedState mLastCookedState; - - // State provided by an external stylus - StylusState mExternalStylusState; - int64_t mExternalStylusId; - nsecs_t mExternalStylusFusionTimeout; - bool mExternalStylusDataPending; - - // True if we sent a HOVER_ENTER event. - bool mSentHoverEnter; - - // Have we assigned pointer IDs for this stream - bool mHavePointerIds; - - // Is the current stream of direct touch events aborted - bool mCurrentMotionAborted; - - // The time the primary pointer last went down. - nsecs_t mDownTime; - - // The pointer controller, or null if the device is not a pointer. - sp mPointerController; - - std::vector mVirtualKeys; - - virtual void configureParameters(); - virtual void dumpParameters(std::string& dump); - virtual void configureRawPointerAxes(); - virtual void dumpRawPointerAxes(std::string& dump); - virtual void configureSurface(nsecs_t when, bool* outResetNeeded); - virtual void dumpSurface(std::string& dump); - virtual void configureVirtualKeys(); - virtual void dumpVirtualKeys(std::string& dump); - virtual void parseCalibration(); - virtual void resolveCalibration(); - virtual void dumpCalibration(std::string& dump); - virtual void updateAffineTransformation(); - virtual void dumpAffineTransformation(std::string& dump); - virtual void resolveExternalStylusPresence(); - virtual bool hasStylus() const = 0; - virtual bool hasExternalStylus() const; - - virtual void syncTouch(nsecs_t when, RawState* outState) = 0; - -private: - // The current viewport. - // The components of the viewport are specified in the display's rotated orientation. - DisplayViewport mViewport; - - // The surface orientation, width and height set by configureSurface(). - // The width and height are derived from the viewport but are specified - // in the natural orientation. - // The surface origin specifies how the surface coordinates should be translated - // to align with the logical display coordinate space. - int32_t mSurfaceWidth; - int32_t mSurfaceHeight; - int32_t mSurfaceLeft; - int32_t mSurfaceTop; - - // Similar to the surface coordinates, but in the raw display coordinate space rather than in - // the logical coordinate space. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; - - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. - int32_t mSurfaceOrientation; - - // Translation and scaling factors, orientation-independent. - float mXTranslate; - float mXScale; - float mXPrecision; - - float mYTranslate; - float mYScale; - float mYPrecision; - - float mGeometricScale; - - float mPressureScale; - - float mSizeScale; - - float mOrientationScale; - - float mDistanceScale; - - bool mHaveTilt; - float mTiltXCenter; - float mTiltXScale; - float mTiltYCenter; - float mTiltYScale; - - bool mExternalStylusConnected; - - // Oriented motion ranges for input device info. - struct OrientedRanges { - InputDeviceInfo::MotionRange x; - InputDeviceInfo::MotionRange y; - InputDeviceInfo::MotionRange pressure; - - bool haveSize; - InputDeviceInfo::MotionRange size; - - bool haveTouchSize; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; - - bool haveToolSize; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; - - bool haveOrientation; - InputDeviceInfo::MotionRange orientation; - - bool haveDistance; - InputDeviceInfo::MotionRange distance; - - bool haveTilt; - InputDeviceInfo::MotionRange tilt; - - OrientedRanges() { - clear(); - } - - void clear() { - haveSize = false; - haveTouchSize = false; - haveToolSize = false; - haveOrientation = false; - haveDistance = false; - haveTilt = false; - } - } mOrientedRanges; - - // Oriented dimensions and precision. - float mOrientedXPrecision; - float mOrientedYPrecision; - - struct CurrentVirtualKeyState { - bool down; - bool ignored; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } mCurrentVirtualKey; - - // Scale factor for gesture or mouse based pointer movements. - float mPointerXMovementScale; - float mPointerYMovementScale; - - // Scale factor for gesture based zooming and other freeform motions. - float mPointerXZoomScale; - float mPointerYZoomScale; - - // The maximum swipe width. - float mPointerGestureMaxSwipeWidth; - - struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance - }; - - enum PointerUsage { - POINTER_USAGE_NONE, - POINTER_USAGE_GESTURES, - POINTER_USAGE_STYLUS, - POINTER_USAGE_MOUSE, - }; - PointerUsage mPointerUsage; - - struct PointerGesture { - enum Mode { - // No fingers, button is not pressed. - // Nothing happening. - NEUTRAL, - - // No fingers, button is not pressed. - // Tap detected. - // Emits DOWN and UP events at the pointer location. - TAP, - - // Exactly one finger dragging following a tap. - // Pointer follows the active finger. - // Emits DOWN, MOVE and UP events at the pointer location. - // - // Detect double-taps when the finger goes up while in TAP_DRAG mode. - TAP_DRAG, - - // Button is pressed. - // Pointer follows the active finger if there is one. Other fingers are ignored. - // Emits DOWN, MOVE and UP events at the pointer location. - BUTTON_CLICK_OR_DRAG, - - // Exactly one finger, button is not pressed. - // Pointer follows the active finger. - // Emits HOVER_MOVE events at the pointer location. - // - // Detect taps when the finger goes up while in HOVER mode. - HOVER, - - // Exactly two fingers but neither have moved enough to clearly indicate - // whether a swipe or freeform gesture was intended. We consider the - // pointer to be pressed so this enables clicking or long-pressing on buttons. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. - PRESS, - - // Exactly two fingers moving in the same direction, button is not pressed. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single pointer coordinate that - // follows the midpoint between both fingers. - SWIPE, - - // Two or more fingers moving in arbitrary directions, button is not pressed. - // Pointer does not move. - // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow - // each finger individually relative to the initial centroid of the finger. - FREEFORM, - - // Waiting for quiet time to end before starting the next gesture. - QUIET, - }; - - // Time the first finger went down. - nsecs_t firstTouchTime; - - // The active pointer id from the raw touch data. - int32_t activeTouchId; // -1 if none - - // The active pointer id from the gesture last delivered to the application. - int32_t activeGestureId; // -1 if none - - // Pointer coords and ids for the current and previous pointer gesture. - Mode currentGestureMode; - BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; - - Mode lastGestureMode; - BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; - - // Time the pointer gesture last went down. - nsecs_t downTime; - - // Time when the pointer went down for a TAP. - nsecs_t tapDownTime; - - // Time when the pointer went up for a TAP. - nsecs_t tapUpTime; - - // Location of initial tap. - float tapX, tapY; - - // Time we started waiting for quiescence. - nsecs_t quietTime; - - // Reference points for multitouch gestures. - float referenceTouchX; // reference touch X/Y coordinates in surface units - float referenceTouchY; - float referenceGestureX; // reference gesture X/Y coordinates in pixels - float referenceGestureY; - - // Distance that each pointer has traveled which has not yet been - // subsumed into the reference gesture position. - BitSet32 referenceIdBits; - struct Delta { - float dx, dy; - }; - Delta referenceDeltas[MAX_POINTER_ID + 1]; - - // Describes how touch ids are mapped to gesture ids for freeform gestures. - uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; - - // A velocity tracker for determining whether to switch active pointers during drags. - VelocityTracker velocityTracker; - - void reset() { - firstTouchTime = LLONG_MIN; - activeTouchId = -1; - activeGestureId = -1; - currentGestureMode = NEUTRAL; - currentGestureIdBits.clear(); - lastGestureMode = NEUTRAL; - lastGestureIdBits.clear(); - downTime = 0; - velocityTracker.clear(); - resetTap(); - resetQuietTime(); - } - - void resetTap() { - tapDownTime = LLONG_MIN; - tapUpTime = LLONG_MIN; - } - - void resetQuietTime() { - quietTime = LLONG_MIN; - } - } mPointerGesture; - - struct PointerSimple { - PointerCoords currentCoords; - PointerProperties currentProperties; - PointerCoords lastCoords; - PointerProperties lastProperties; - - // True if the pointer is down. - bool down; - - // True if the pointer is hovering. - bool hovering; - - // Time the pointer last went down. - nsecs_t downTime; - - void reset() { - currentCoords.clear(); - currentProperties.clear(); - lastCoords.clear(); - lastProperties.clear(); - down = false; - hovering = false; - downTime = 0; - } - } mPointerSimple; - - // The pointer and scroll velocity controls. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - std::optional findViewport(); - - void resetExternalStylus(); - void clearStylusDataPendingFlags(); - - void sync(nsecs_t when); - - bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); - void processRawTouches(bool timeout); - void cookAndDispatch(nsecs_t when); - void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags); - - void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); - void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); - void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); - void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); - const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); - void cookPointerData(); - void abortTouches(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); - void abortPointerUsage(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); - void abortPointerGestures(nsecs_t when, uint32_t policyFlags); - bool preparePointerGestures(nsecs_t when, - bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, - bool isTimeout); - - void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); - void abortPointerStylus(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); - void abortPointerMouse(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, - bool down, bool hovering); - void abortPointerSimple(nsecs_t when, uint32_t policyFlags); - - bool assignExternalStylusId(const RawState& state, bool timeout); - void applyExternalStylusButtonState(nsecs_t when); - void applyExternalStylusTouchState(nsecs_t when); - - // Dispatches a motion event. - // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the - // method will take care of setting the index and transmuting the action to DOWN or UP - // it is the first / last pointer to go down / up. - void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, - const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, - int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); - - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, const uint32_t* inIdToIndex, - PointerProperties* outProperties, PointerCoords* outCoords, - const uint32_t* outIdToIndex, BitSet32 idBits) const; - - bool isPointInsideSurface(int32_t x, int32_t y); - const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); - - static void assignPointerIds(const RawState* last, RawState* current); - - const char* modeToString(DeviceMode deviceMode); -}; - - -class SingleTouchInputMapper : public TouchInputMapper { -public: - explicit SingleTouchInputMapper(InputDevice* device); - virtual ~SingleTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; -}; - - -class MultiTouchInputMapper : public TouchInputMapper { -public: - explicit MultiTouchInputMapper(InputDevice* device); - virtual ~MultiTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; - - // Specifies the pointer id bits that are in use, and their associated tracking id. - BitSet32 mPointerIdBits; - int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; -}; - -class ExternalStylusInputMapper : public InputMapper { -public: - explicit ExternalStylusInputMapper(InputDevice* device); - virtual ~ExternalStylusInputMapper() = default; - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; - RawAbsoluteAxisInfo mRawPressureAxis; - TouchButtonAccumulator mTouchButtonAccumulator; - - StylusState mStylusState; -}; - - -class JoystickInputMapper : public InputMapper { -public: - explicit JoystickInputMapper(InputDevice* device); - virtual ~JoystickInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - struct Axis { - RawAbsoluteAxisInfo rawAxisInfo; - AxisInfo axisInfo; - - bool explicitlyMapped; // true if the axis was explicitly assigned an axis id - - float scale; // scale factor from raw to normalized values - float offset; // offset to add after scaling for normalization - float highScale; // scale factor from raw to normalized values of high split - float highOffset; // offset to add after scaling for normalization of high split - - float min; // normalized inclusive minimum - float max; // normalized inclusive maximum - float flat; // normalized flat region size - float fuzz; // normalized error tolerance - float resolution; // normalized resolution in units/mm - - float filter; // filter out small variations of this size - float currentValue; // current value - float newValue; // most recent value - float highCurrentValue; // current value of high split - float highNewValue; // most recent value of high split - - void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, - bool explicitlyMapped, float scale, float offset, - float highScale, float highOffset, - float min, float max, float flat, float fuzz, float resolution) { - this->rawAxisInfo = rawAxisInfo; - this->axisInfo = axisInfo; - this->explicitlyMapped = explicitlyMapped; - this->scale = scale; - this->offset = offset; - this->highScale = highScale; - this->highOffset = highOffset; - this->min = min; - this->max = max; - this->flat = flat; - this->fuzz = fuzz; - this->resolution = resolution; - this->filter = 0; - resetValue(); - } - - void resetValue() { - this->currentValue = 0; - this->newValue = 0; - this->highCurrentValue = 0; - this->highNewValue = 0; - } - }; - - // Axes indexed by raw ABS_* axis index. - KeyedVector mAxes; - - void sync(nsecs_t when, bool force); - - bool haveAxis(int32_t axisId); - void pruneAxes(bool ignoreExplicitlyMappedAxes); - bool filterAxes(bool force); - - static bool hasValueChangedSignificantly(float filter, - float newValue, float currentValue, float min, float max); - static bool hasMovedNearerToValueWithinFilteredRange(float filter, - float newValue, float currentValue, float thresholdValue); - - static bool isCenteredAxis(int32_t axis); - static int32_t getCompatAxis(int32_t axis); - - static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); - static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, - float value); -}; - -} // namespace android - -#endif // _UI_INPUT_READER_H diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/InputReaderFactory.cpp deleted file mode 100644 index 072499b9cd..0000000000 --- a/services/inputflinger/InputReaderFactory.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InputReaderFactory.h" -#include "InputReader.h" - -namespace android { - -sp createInputReader( - const sp& policy, - const sp& listener) { - return new InputReader(std::make_unique(), policy, listener); -} - -} // namespace android \ No newline at end of file diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/TouchVideoDevice.cpp deleted file mode 100644 index 19c1313dcd..0000000000 --- a/services/inputflinger/TouchVideoDevice.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TouchVideoDevice.h" - -#define LOG_TAG "TouchVideoDevice" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using android::base::StringPrintf; -using android::base::unique_fd; - -namespace android { - -TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t height, uint32_t width, - const std::array& readLocations) : - mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)), - mHeight(height), mWidth(width), - mReadLocations(readLocations) { - mFrames.reserve(MAX_QUEUE_SIZE); -}; - -std::unique_ptr TouchVideoDevice::create(std::string devicePath) { - unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK)); - if (fd.get() == INVALID_FD) { - ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno)); - return nullptr; - } - - struct v4l2_capability cap; - int result = ioctl(fd.get(), VIDIOC_QUERYCAP, &cap); - if (result == -1) { - ALOGE("VIDIOC_QUERYCAP failed: %s", strerror(errno)); - return nullptr; - } - if (!(cap.capabilities & V4L2_CAP_TOUCH)) { - ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. " - "Make sure device specifies V4L2_CAP_TOUCH"); - return nullptr; - } - ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", - cap.driver, cap.card, cap.bus_info, cap.version); - std::string name = reinterpret_cast(cap.card); - - struct v4l2_input v4l2_input_struct; - v4l2_input_struct.index = 0; - result = ioctl(fd.get(), VIDIOC_ENUMINPUT, &v4l2_input_struct); - if (result == -1) { - ALOGE("VIDIOC_ENUMINPUT failed: %s", strerror(errno)); - return nullptr; - } - - if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) { - ALOGE("Video device does not provide touch data. " - "Make sure device specifies V4L2_INPUT_TYPE_TOUCH."); - return nullptr; - } - - struct v4l2_format v4l2_fmt; - v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - result = ioctl(fd.get(), VIDIOC_G_FMT, &v4l2_fmt); - if (result == -1) { - ALOGE("VIDIOC_G_FMT failed: %s", strerror(errno)); - return nullptr; - } - const uint32_t height = v4l2_fmt.fmt.pix.height; - const uint32_t width = v4l2_fmt.fmt.pix.width; - ALOGI("Frame dimensions: height = %" PRIu32 " width = %" PRIu32, height, width); - - struct v4l2_requestbuffers req = {}; - req.count = NUM_BUFFERS; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; - // req.reserved is zeroed during initialization, which is required per v4l docs - result = ioctl(fd.get(), VIDIOC_REQBUFS, &req); - if (result == -1) { - ALOGE("VIDIOC_REQBUFS failed: %s", strerror(errno)); - return nullptr; - } - if (req.count != NUM_BUFFERS) { - ALOGE("Requested %zu buffers, but driver responded with count=%i", NUM_BUFFERS, req.count); - return nullptr; - } - - struct v4l2_buffer buf = {}; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - // buf.reserved and buf.reserved2 are zeroed during initialization, required per v4l docs - std::array readLocations; - for (size_t i = 0; i < NUM_BUFFERS; i++) { - buf.index = i; - result = ioctl(fd.get(), VIDIOC_QUERYBUF, &buf); - if (result == -1) { - ALOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno)); - return nullptr; - } - if (buf.length != height * width * sizeof(int16_t)) { - ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", - buf.length, buf.m.offset); - return nullptr; - } - - readLocations[i] = static_cast(mmap(nullptr /* start anywhere */, - buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */, - fd.get(), buf.m.offset)); - if (readLocations[i] == MAP_FAILED) { - ALOGE("%s: map failed: %s", __func__, strerror(errno)); - return nullptr; - } - } - - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - result = ioctl(fd.get(), VIDIOC_STREAMON, &type); - if (result == -1) { - ALOGE("VIDIOC_STREAMON failed: %s", strerror(errno)); - return nullptr; - } - - for (size_t i = 0; i < NUM_BUFFERS; i++) { - buf.index = i; - result = ioctl(fd.get(), VIDIOC_QBUF, &buf); - if (result == -1) { - ALOGE("VIDIOC_QBUF failed for buffer %zu: %s", i, strerror(errno)); - return nullptr; - } - } - // Using 'new' to access a non-public constructor. - return std::unique_ptr(new TouchVideoDevice( - fd.release(), std::move(name), std::move(devicePath), height, width, readLocations)); -} - -size_t TouchVideoDevice::readAndQueueFrames() { - std::vector frames = readFrames(); - const size_t numFrames = frames.size(); - if (numFrames == 0) { - // Likely an error occurred - return 0; - } - // Concatenate the vectors, then clip up to maximum size allowed - mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()), - std::make_move_iterator(frames.end())); - if (mFrames.size() > MAX_QUEUE_SIZE) { - ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE, - mFrames.size() - MAX_QUEUE_SIZE); - mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE); - } - return numFrames; -} - -std::vector TouchVideoDevice::consumeFrames() { - std::vector frames = std::move(mFrames); - mFrames = {}; - return frames; -} - -std::optional TouchVideoDevice::readFrame() { - struct v4l2_buffer buf = {}; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - int result = ioctl(mFd.get(), VIDIOC_DQBUF, &buf); - if (result == -1) { - // EAGAIN means we've reached the end of the read buffer, so it's expected. - if (errno != EAGAIN) { - ALOGE("VIDIOC_DQBUF failed: %s", strerror(errno)); - } - return std::nullopt; - } - if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) { - // We use CLOCK_MONOTONIC for input events, so if the clocks don't match, - // we can't compare timestamps. Just log a warning, since this is a driver issue - ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", - buf.timestamp.tv_sec, buf.timestamp.tv_usec); - } - std::vector data(mHeight * mWidth); - const int16_t* readFrom = mReadLocations[buf.index]; - std::copy(readFrom, readFrom + mHeight * mWidth, data.begin()); - TouchVideoFrame frame(mHeight, mWidth, std::move(data), buf.timestamp); - - result = ioctl(mFd.get(), VIDIOC_QBUF, &buf); - if (result == -1) { - ALOGE("VIDIOC_QBUF failed: %s", strerror(errno)); - } - return std::make_optional(std::move(frame)); -} - -/* - * This function should not be called unless buffer is ready! This must be checked with - * select, poll, epoll, or some other similar api first. - * The oldest frame will be at the beginning of the array. - */ -std::vector TouchVideoDevice::readFrames() { - std::vector frames; - while (true) { - std::optional frame = readFrame(); - if (!frame) { - break; - } - frames.push_back(std::move(*frame)); - } - return frames; -} - -TouchVideoDevice::~TouchVideoDevice() { - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int result = ioctl(mFd.get(), VIDIOC_STREAMOFF, &type); - if (result == -1) { - ALOGE("VIDIOC_STREAMOFF failed: %s", strerror(errno)); - } - for (const int16_t* buffer : mReadLocations) { - void* bufferAddress = static_cast(const_cast(buffer)); - result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t)); - if (result == -1) { - ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno)); - } - } -} - -std::string TouchVideoDevice::dump() const { - return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32 - ", fd=%i, hasValidFd=%s", - mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(), - hasValidFd() ? "true" : "false"); -} - -} // namespace android diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/TouchVideoDevice.h deleted file mode 100644 index 0e7e2ef496..0000000000 --- a/services/inputflinger/TouchVideoDevice.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H -#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/** - * Represents a video device that uses v4l2 api to report touch heatmap data. - */ -class TouchVideoDevice { -public: - /** - * Create a new TouchVideoDevice for the path provided. - * Return nullptr upon failure. - */ - static std::unique_ptr create(std::string devicePath); - ~TouchVideoDevice(); - - bool hasValidFd() const { return mFd.get() != INVALID_FD; } - /** - * Obtain the file descriptor associated with this video device. - * Could be used for adding to epoll. - */ - int getFd() const { return mFd.get(); } - /** - * Get the name of this video device. - */ - const std::string& getName() const { return mName; } - /** - * Get the file path of this video device. - */ - const std::string& getPath() const { return mPath; } - /** - * Get the height of the heatmap frame - */ - uint32_t getHeight() const { return mHeight; } - /** - * Get the width of the heatmap frame - */ - uint32_t getWidth() const { return mWidth; } - /** - * Direct read of the frame. Stores the frame into internal buffer. - * Return the number of frames that were successfully read. - * - * This function should not be called unless buffer is ready! - * This must be checked with select, poll, epoll, or similar api first. - * If epoll indicates that there is data ready to read, but this function - * returns zero, then it is likely an error occurred. - */ - size_t readAndQueueFrames(); - /** - * Return all of the queued frames, and erase them from the local buffer. - * The returned frames are in the order that they were received from the - * v4l2 device, with the oldest frame at the index 0. - */ - std::vector consumeFrames(); - /** - * Get string representation of this video device. - */ - std::string dump() const; - -private: - android::base::unique_fd mFd; - std::string mName; - std::string mPath; - - uint32_t mHeight; - uint32_t mWidth; - - static constexpr int INVALID_FD = -1; - /** - * How many buffers to request for heatmap. - * The kernel driver will be allocating these buffers for us, - * and will provide memory locations to read these from. - */ - static constexpr size_t NUM_BUFFERS = 3; - std::array mReadLocations; - /** - * How many buffers to keep for the internal queue. When the internal buffer - * exceeds this capacity, oldest frames will be dropped. - */ - static constexpr size_t MAX_QUEUE_SIZE = 10; - std::vector mFrames; - - /** - * The constructor is private because opening a v4l2 device requires many checks. - * To get a new TouchVideoDevice, use 'create' instead. - */ - explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t height, uint32_t width, - const std::array& readLocations); - /** - * Read all currently available frames. - */ - std::vector readFrames(); - /** - * Read a single frame. May return nullopt if no data is currently available for reading. - */ - std::optional readFrame(); -}; -} // namespace android -#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp new file mode 100644 index 0000000000..c3e8ceda56 --- /dev/null +++ b/services/inputflinger/reader/Android.bp @@ -0,0 +1,51 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_headers { + name: "libinputreader_headers", + export_include_dirs: ["include"], +} + +cc_library_shared { + name: "libinputreader", + defaults: ["inputflinger_defaults"], + + srcs: [ + "EventHub.cpp", + "InputReader.cpp", + "InputReaderFactory.cpp", + "TouchVideoDevice.cpp", + ], + + shared_libs: [ + "libbase", + "libinputflinger_base", + "libcrypto", + "libcutils", + "libinput", + "liblog", + "libui", + "libutils", + "libhardware_legacy", + ], + + header_libs: [ + "libinputflinger_headers", + "libinputreader_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], +} diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp new file mode 100644 index 0000000000..c15ecfd66b --- /dev/null +++ b/services/inputflinger/reader/EventHub.cpp @@ -0,0 +1,1945 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "EventHub" + +// #define LOG_NDEBUG 0 + +#include "EventHub.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* this macro is used to tell if "bit" is set in "array" + * it selects a byte from the array, and does a boolean AND + * operation with a byte that only has the relevant bit set. + * eg. to check for the 12th bit, we do (array[1] & 1<<4) + */ +#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8))) + +/* this macro computes the number of bytes needed to represent a bit array of the specified size */ +#define sizeof_bit_array(bits) (((bits) + 7) / 8) + +#define INDENT " " +#define INDENT2 " " +#define INDENT3 " " + +using android::base::StringPrintf; + +namespace android { + +static constexpr bool DEBUG = false; + +static const char* WAKE_LOCK_ID = "KeyEvents"; +static const char* DEVICE_PATH = "/dev/input"; +// v4l2 devices go directly into /dev +static const char* VIDEO_DEVICE_PATH = "/dev"; + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +static std::string sha1(const std::string& in) { + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, reinterpret_cast(in.c_str()), in.size()); + u_char digest[SHA_DIGEST_LENGTH]; + SHA1_Final(digest, &ctx); + + std::string out; + for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { + out += StringPrintf("%02x", digest[i]); + } + return out; +} + +/** + * Return true if name matches "v4l-touch*" + */ +static bool isV4lTouchNode(const char* name) { + return strstr(name, "v4l-touch") == name; +} + +/** + * Returns true if V4L devices should be scanned. + * + * The system property ro.input.video_enabled can be used to control whether + * EventHub scans and opens V4L devices. As V4L does not support multiple + * clients, EventHub effectively blocks access to these devices when it opens + * them. + * + * Setting this to "false" would prevent any video devices from being discovered and + * associated with input devices. + * + * This property can be used as follows: + * 1. To turn off features that are dependent on video device presence. + * 2. During testing and development, to allow other clients to read video devices + * directly from /dev. + */ +static bool isV4lScanningEnabled() { + return property_get_bool("ro.input.video_enabled", true /* default_value */); +} + +static nsecs_t processEventTimestamp(const struct input_event& event) { + // Use the time specified in the event instead of the current time + // so that downstream code can get more accurate estimates of + // event dispatch latency from the time the event is enqueued onto + // the evdev client buffer. + // + // The event's timestamp fortuitously uses the same monotonic clock + // time base as the rest of Android. The kernel event device driver + // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). + // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere + // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a + // system call that also queries ktime_get_ts(). + + const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) + + microseconds_to_nanoseconds(event.time.tv_usec); + return inputEventTime; +} + +// --- Global Functions --- + +uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { + // Touch devices get dibs on touch-related axes. + if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) { + switch (axis) { + case ABS_X: + case ABS_Y: + case ABS_PRESSURE: + case ABS_TOOL_WIDTH: + case ABS_DISTANCE: + case ABS_TILT_X: + case ABS_TILT_Y: + case ABS_MT_SLOT: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: + case ABS_MT_WIDTH_MAJOR: + case ABS_MT_WIDTH_MINOR: + case ABS_MT_ORIENTATION: + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + case ABS_MT_TOOL_TYPE: + case ABS_MT_BLOB_ID: + case ABS_MT_TRACKING_ID: + case ABS_MT_PRESSURE: + case ABS_MT_DISTANCE: + return INPUT_DEVICE_CLASS_TOUCH; + } + } + + // External stylus gets the pressure axis + if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (axis == ABS_PRESSURE) { + return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; + } + } + + // Joystick devices get the rest. + return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK; +} + +// --- EventHub::Device --- + +EventHub::Device::Device(int fd, int32_t id, const std::string& path, + const InputDeviceIdentifier& identifier) + : next(nullptr), + fd(fd), + id(id), + path(path), + identifier(identifier), + classes(0), + configuration(nullptr), + virtualKeyMap(nullptr), + ffEffectPlaying(false), + ffEffectId(-1), + controllerNumber(0), + enabled(true), + isVirtual(fd < 0) { + memset(keyBitmask, 0, sizeof(keyBitmask)); + memset(absBitmask, 0, sizeof(absBitmask)); + memset(relBitmask, 0, sizeof(relBitmask)); + memset(swBitmask, 0, sizeof(swBitmask)); + memset(ledBitmask, 0, sizeof(ledBitmask)); + memset(ffBitmask, 0, sizeof(ffBitmask)); + memset(propBitmask, 0, sizeof(propBitmask)); +} + +EventHub::Device::~Device() { + close(); + delete configuration; +} + +void EventHub::Device::close() { + if (fd >= 0) { + ::close(fd); + fd = -1; + } +} + +status_t EventHub::Device::enable() { + fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); + if (fd < 0) { + ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno)); + return -errno; + } + enabled = true; + return OK; +} + +status_t EventHub::Device::disable() { + close(); + enabled = false; + return OK; +} + +bool EventHub::Device::hasValidFd() { + return !isVirtual && enabled; +} + +// --- EventHub --- + +const int EventHub::EPOLL_MAX_EVENTS; + +EventHub::EventHub(void) + : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), + mNextDeviceId(1), + mControllerNumbers(), + mOpeningDevices(nullptr), + mClosingDevices(nullptr), + mNeedToSendFinishedDeviceScan(false), + mNeedToReopenDevices(false), + mNeedToScanDevices(true), + mPendingEventCount(0), + mPendingEventIndex(0), + mPendingINotify(false) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + mEpollFd = epoll_create1(EPOLL_CLOEXEC); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); + + mINotifyFd = inotify_init(); + mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); + LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH, + strerror(errno)); + if (isV4lScanningEnabled()) { + mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE); + LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s", + VIDEO_DEVICE_PATH, strerror(errno)); + } else { + mVideoWd = -1; + ALOGI("Video device scanning disabled"); + } + + struct epoll_event eventItem; + memset(&eventItem, 0, sizeof(eventItem)); + eventItem.events = EPOLLIN; + eventItem.data.fd = mINotifyFd; + int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); + + int wakeFds[2]; + result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + eventItem.data.fd = mWakeReadPipeFd; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", + errno); +} + +EventHub::~EventHub(void) { + closeAllDevicesLocked(); + + while (mClosingDevices) { + Device* device = mClosingDevices; + mClosingDevices = device->next; + delete device; + } + + ::close(mEpollFd); + ::close(mINotifyFd); + ::close(mWakeReadPipeFd); + ::close(mWakeWritePipeFd); + + release_wake_lock(WAKE_LOCK_ID); +} + +InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) return InputDeviceIdentifier(); + return device->identifier; +} + +uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) return 0; + return device->classes; +} + +int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) return 0; + return device->controllerNumber; +} + +void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->configuration) { + *outConfiguration = *device->configuration; + } else { + outConfiguration->clear(); + } +} + +status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const { + outAxisInfo->clear(); + + if (axis >= 0 && axis <= ABS_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { + struct input_absinfo info; + if (ioctl(device->fd, EVIOCGABS(axis), &info)) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, + device->identifier.name.c_str(), device->fd, errno); + return -errno; + } + + if (info.minimum != info.maximum) { + outAxisInfo->valid = true; + outAxisInfo->minValue = info.minimum; + outAxisInfo->maxValue = info.maximum; + outAxisInfo->flat = info.flat; + outAxisInfo->fuzz = info.fuzz; + outAxisInfo->resolution = info.resolution; + } + return OK; + } + } + return -1; +} + +bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { + if (axis >= 0 && axis <= REL_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device) { + return test_bit(axis, device->relBitmask); + } + } + return false; +} + +bool EventHub::hasInputProperty(int32_t deviceId, int property) const { + if (property >= 0 && property <= INPUT_PROP_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device) { + return test_bit(property, device->propBitmask); + } + } + return false; +} + +int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { + if (scanCode >= 0 && scanCode <= KEY_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) { + uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; + memset(keyState, 0, sizeof(keyState)); + if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { + return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + } + } + return AKEY_STATE_UNKNOWN; +} + +int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) { + std::vector scanCodes; + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + if (scanCodes.size() != 0) { + uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; + memset(keyState, 0, sizeof(keyState)); + if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { + for (size_t i = 0; i < scanCodes.size(); i++) { + int32_t sc = scanCodes[i]; + if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) { + return AKEY_STATE_DOWN; + } + } + return AKEY_STATE_UP; + } + } + } + return AKEY_STATE_UNKNOWN; +} + +int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { + if (sw >= 0 && sw <= SW_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) { + uint8_t swState[sizeof_bit_array(SW_MAX + 1)]; + memset(swState, 0, sizeof(swState)); + if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) { + return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + } + } + } + return AKEY_STATE_UNKNOWN; +} + +status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const { + *outValue = 0; + + if (axis >= 0 && axis <= ABS_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { + struct input_absinfo info; + if (ioctl(device->fd, EVIOCGABS(axis), &info)) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, + device->identifier.name.c_str(), device->fd, errno); + return -errno; + } + + *outValue = info.value; + return OK; + } + } + return -1; +} + +bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->keyMap.haveKeyLayout()) { + std::vector scanCodes; + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + scanCodes.clear(); + + status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], + &scanCodes); + if (!err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], device->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + return true; + } + return false; +} + +status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + status_t status = NAME_NOT_FOUND; + + if (device) { + // Check the key character map first. + sp kcm = device->getKeyCharacterMap(); + if (kcm != nullptr) { + if (!kcm->mapKey(scanCode, usageCode, outKeycode)) { + *outFlags = 0; + status = NO_ERROR; + } + } + + // Check the key layout next. + if (status != NO_ERROR && device->keyMap.haveKeyLayout()) { + if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) { + status = NO_ERROR; + } + } + + if (status == NO_ERROR) { + if (kcm != nullptr) { + kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState); + } else { + *outMetaState = metaState; + } + } + } + + if (status != NO_ERROR) { + *outKeycode = 0; + *outFlags = 0; + *outMetaState = metaState; + } + + return status; +} + +status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + + if (device && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void EventHub::setExcludedDevices(const std::vector& devices) { + AutoMutex _l(mLock); + + mExcludedDevices = devices; +} + +bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && scanCode >= 0 && scanCode <= KEY_MAX) { + if (test_bit(scanCode, device->keyBitmask)) { + return true; + } + } + return false; +} + +bool EventHub::hasLed(int32_t deviceId, int32_t led) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + int32_t sc; + if (device && mapLed(device, led, &sc) == NO_ERROR) { + if (test_bit(sc, device->ledBitmask)) { + return true; + } + } + return false; +} + +void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + setLedStateLocked(device, led, on); +} + +void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) { + int32_t sc; + if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) { + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_LED; + ev.code = sc; + ev.value = on ? 1 : 0; + + ssize_t nWrite; + do { + nWrite = write(device->fd, &ev, sizeof(struct input_event)); + } while (nWrite == -1 && errno == EINTR); + } +} + +void EventHub::getVirtualKeyDefinitions(int32_t deviceId, + std::vector& outVirtualKeys) const { + outVirtualKeys.clear(); + + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->virtualKeyMap) { + const std::vector virtualKeys = + device->virtualKeyMap->getVirtualKeys(); + outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end()); + } +} + +sp EventHub::getKeyCharacterMap(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device) { + return device->getKeyCharacterMap(); + } + return nullptr; +} + +bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp& map) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device) { + if (map != device->overlayKeyMap) { + device->overlayKeyMap = map; + device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map); + return true; + } + } + return false; +} + +static std::string generateDescriptor(InputDeviceIdentifier& identifier) { + std::string rawDescriptor; + rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, identifier.product); + // TODO add handling for USB devices to not uniqueify kbs that show up twice + if (!identifier.uniqueId.empty()) { + rawDescriptor += "uniqueId:"; + rawDescriptor += identifier.uniqueId; + } else if (identifier.nonce != 0) { + rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce); + } + + if (identifier.vendor == 0 && identifier.product == 0) { + // If we don't know the vendor and product id, then the device is probably + // built-in so we need to rely on other information to uniquely identify + // the input device. Usually we try to avoid relying on the device name or + // location but for built-in input device, they are unlikely to ever change. + if (!identifier.name.empty()) { + rawDescriptor += "name:"; + rawDescriptor += identifier.name; + } else if (!identifier.location.empty()) { + rawDescriptor += "location:"; + rawDescriptor += identifier.location; + } + } + identifier.descriptor = sha1(rawDescriptor); + return rawDescriptor; +} + +void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { + // Compute a device descriptor that uniquely identifies the device. + // The descriptor is assumed to be a stable identifier. Its value should not + // change between reboots, reconnections, firmware updates or new releases + // of Android. In practice we sometimes get devices that cannot be uniquely + // identified. In this case we enforce uniqueness between connected devices. + // Ideally, we also want the descriptor to be short and relatively opaque. + + identifier.nonce = 0; + std::string rawDescriptor = generateDescriptor(identifier); + if (identifier.uniqueId.empty()) { + // If it didn't have a unique id check for conflicts and enforce + // uniqueness if necessary. + while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { + identifier.nonce++; + rawDescriptor = generateDescriptor(identifier); + } + } + ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), + identifier.descriptor.c_str()); +} + +void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd()) { + ff_effect effect; + memset(&effect, 0, sizeof(effect)); + effect.type = FF_RUMBLE; + effect.id = device->ffEffectId; + effect.u.rumble.strong_magnitude = 0xc000; + effect.u.rumble.weak_magnitude = 0xc000; + effect.replay.length = (duration + 999999LL) / 1000000LL; + effect.replay.delay = 0; + if (ioctl(device->fd, EVIOCSFF, &effect)) { + ALOGW("Could not upload force feedback effect to device %s due to error %d.", + device->identifier.name.c_str(), errno); + return; + } + device->ffEffectId = effect.id; + + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_FF; + ev.code = device->ffEffectId; + ev.value = 1; + if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { + ALOGW("Could not start force feedback effect on device %s due to error %d.", + device->identifier.name.c_str(), errno); + return; + } + device->ffEffectPlaying = true; + } +} + +void EventHub::cancelVibrate(int32_t deviceId) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && device->hasValidFd()) { + if (device->ffEffectPlaying) { + device->ffEffectPlaying = false; + + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_FF; + ev.code = device->ffEffectId; + ev.value = 0; + if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { + ALOGW("Could not stop force feedback effect on device %s due to error %d.", + device->identifier.name.c_str(), errno); + return; + } + } + } +} + +EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { + size_t size = mDevices.size(); + for (size_t i = 0; i < size; i++) { + Device* device = mDevices.valueAt(i); + if (descriptor == device->identifier.descriptor) { + return device; + } + } + return nullptr; +} + +EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { + if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) { + deviceId = mBuiltInKeyboardId; + } + ssize_t index = mDevices.indexOfKey(deviceId); + return index >= 0 ? mDevices.valueAt(index) : NULL; +} + +EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const { + for (size_t i = 0; i < mDevices.size(); i++) { + Device* device = mDevices.valueAt(i); + if (device->path == devicePath) { + return device; + } + } + return nullptr; +} + +/** + * The file descriptor could be either input device, or a video device (associated with a + * specific input device). Check both cases here, and return the device that this event + * belongs to. Caller can compare the fd's once more to determine event type. + * Looks through all input devices, and only attached video devices. Unattached video + * devices are ignored. + */ +EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const { + for (size_t i = 0; i < mDevices.size(); i++) { + Device* device = mDevices.valueAt(i); + if (device->fd == fd) { + // This is an input device event + return device; + } + if (device->videoDevice && device->videoDevice->getFd() == fd) { + // This is a video device event + return device; + } + } + // We do not check mUnattachedVideoDevices here because they should not participate in epoll, + // and therefore should never be looked up by fd. + return nullptr; +} + +size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { + ALOG_ASSERT(bufferSize >= 1); + + AutoMutex _l(mLock); + + struct input_event readBuffer[bufferSize]; + + RawEvent* event = buffer; + size_t capacity = bufferSize; + bool awoken = false; + for (;;) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + // Reopen input devices if needed. + if (mNeedToReopenDevices) { + mNeedToReopenDevices = false; + + ALOGI("Reopening all input devices due to a configuration change."); + + closeAllDevicesLocked(); + mNeedToScanDevices = true; + break; // return to the caller before we actually rescan + } + + // Report any devices that had last been added/removed. + while (mClosingDevices) { + Device* device = mClosingDevices; + ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str()); + mClosingDevices = device->next; + event->when = now; + event->deviceId = (device->id == mBuiltInKeyboardId) + ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID + : device->id; + event->type = DEVICE_REMOVED; + event += 1; + delete device; + mNeedToSendFinishedDeviceScan = true; + if (--capacity == 0) { + break; + } + } + + if (mNeedToScanDevices) { + mNeedToScanDevices = false; + scanDevicesLocked(); + mNeedToSendFinishedDeviceScan = true; + } + + while (mOpeningDevices != nullptr) { + Device* device = mOpeningDevices; + ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str()); + mOpeningDevices = device->next; + event->when = now; + event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + event->type = DEVICE_ADDED; + event += 1; + mNeedToSendFinishedDeviceScan = true; + if (--capacity == 0) { + break; + } + } + + if (mNeedToSendFinishedDeviceScan) { + mNeedToSendFinishedDeviceScan = false; + event->when = now; + event->type = FINISHED_DEVICE_SCAN; + event += 1; + if (--capacity == 0) { + break; + } + } + + // Grab the next input event. + bool deviceChanged = false; + while (mPendingEventIndex < mPendingEventCount) { + const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; + if (eventItem.data.fd == mINotifyFd) { + if (eventItem.events & EPOLLIN) { + mPendingINotify = true; + } else { + ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); + } + continue; + } + + if (eventItem.data.fd == mWakeReadPipeFd) { + if (eventItem.events & EPOLLIN) { + ALOGV("awoken after wake()"); + awoken = true; + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + } else { + ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", + eventItem.events); + } + continue; + } + + Device* device = getDeviceByFdLocked(eventItem.data.fd); + if (!device) { + ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events, + eventItem.data.fd); + ALOG_ASSERT(!DEBUG); + continue; + } + if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) { + if (eventItem.events & EPOLLIN) { + size_t numFrames = device->videoDevice->readAndQueueFrames(); + if (numFrames == 0) { + ALOGE("Received epoll event for video device %s, but could not read frame", + device->videoDevice->getName().c_str()); + } + } else if (eventItem.events & EPOLLHUP) { + // TODO(b/121395353) - consider adding EPOLLRDHUP + ALOGI("Removing video device %s due to epoll hang-up event.", + device->videoDevice->getName().c_str()); + unregisterVideoDeviceFromEpollLocked(*device->videoDevice); + device->videoDevice = nullptr; + } else { + ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, + device->videoDevice->getName().c_str()); + ALOG_ASSERT(!DEBUG); + } + continue; + } + // This must be an input event + if (eventItem.events & EPOLLIN) { + int32_t readSize = + read(device->fd, readBuffer, sizeof(struct input_event) * capacity); + if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { + // Device was removed before INotify noticed. + ALOGW("could not get event, removed? (fd: %d size: %" PRId32 + " bufferSize: %zu capacity: %zu errno: %d)\n", + device->fd, readSize, bufferSize, capacity, errno); + deviceChanged = true; + closeDeviceLocked(device); + } else if (readSize < 0) { + if (errno != EAGAIN && errno != EINTR) { + ALOGW("could not get event (errno=%d)", errno); + } + } else if ((readSize % sizeof(struct input_event)) != 0) { + ALOGE("could not get event (wrong size: %d)", readSize); + } else { + int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + + size_t count = size_t(readSize) / sizeof(struct input_event); + for (size_t i = 0; i < count; i++) { + struct input_event& iev = readBuffer[i]; + event->when = processEventTimestamp(iev); + event->deviceId = deviceId; + event->type = iev.type; + event->code = iev.code; + event->value = iev.value; + event += 1; + capacity -= 1; + } + if (capacity == 0) { + // The result buffer is full. Reset the pending event index + // so we will try to read the device again on the next iteration. + mPendingEventIndex -= 1; + break; + } + } + } else if (eventItem.events & EPOLLHUP) { + ALOGI("Removing device %s due to epoll hang-up event.", + device->identifier.name.c_str()); + deviceChanged = true; + closeDeviceLocked(device); + } else { + ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, + device->identifier.name.c_str()); + } + } + + // readNotify() will modify the list of devices so this must be done after + // processing all other events to ensure that we read all remaining events + // before closing the devices. + if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { + mPendingINotify = false; + readNotifyLocked(); + deviceChanged = true; + } + + // Report added or removed devices immediately. + if (deviceChanged) { + continue; + } + + // Return now if we have collected any events or if we were explicitly awoken. + if (event != buffer || awoken) { + break; + } + + // Poll for events. Mind the wake lock dance! + // We hold a wake lock at all times except during epoll_wait(). This works due to some + // subtle choreography. When a device driver has pending (unread) events, it acquires + // a kernel wake lock. However, once the last pending event has been read, the device + // driver will release the kernel wake lock. To prevent the system from going to sleep + // when this happens, the EventHub holds onto its own user wake lock while the client + // is processing events. Thus the system can only sleep if there are no events + // pending or currently being processed. + // + // The timeout is advisory only. If the device is asleep, it will not wake just to + // service the timeout. + mPendingEventIndex = 0; + + mLock.unlock(); // release lock before poll, must be before release_wake_lock + release_wake_lock(WAKE_LOCK_ID); + + int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock + + if (pollResult == 0) { + // Timed out. + mPendingEventCount = 0; + break; + } + + if (pollResult < 0) { + // An error occurred. + mPendingEventCount = 0; + + // Sleep after errors to avoid locking up the system. + // Hopefully the error is transient. + if (errno != EINTR) { + ALOGW("poll failed (errno=%d)\n", errno); + usleep(100000); + } + } else { + // Some events occurred. + mPendingEventCount = size_t(pollResult); + } + } + + // All done, return the number of events we read. + return event - buffer; +} + +std::vector EventHub::getVideoFrames(int32_t deviceId) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (!device || !device->videoDevice) { + return {}; + } + return device->videoDevice->consumeFrames(); +} + +void EventHub::wake() { + ALOGV("wake() called"); + + ssize_t nWrite; + do { + nWrite = write(mWakeWritePipeFd, "W", 1); + } while (nWrite == -1 && errno == EINTR); + + if (nWrite != 1 && errno != EAGAIN) { + ALOGW("Could not write wake signal: %s", strerror(errno)); + } +} + +void EventHub::scanDevicesLocked() { + status_t result = scanDirLocked(DEVICE_PATH); + if (result < 0) { + ALOGE("scan dir failed for %s", DEVICE_PATH); + } + if (isV4lScanningEnabled()) { + result = scanVideoDirLocked(VIDEO_DEVICE_PATH); + if (result != OK) { + ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH); + } + } + if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) { + createVirtualKeyboardLocked(); + } +} + +// ---------------------------------------------------------------------------- + +static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { + const uint8_t* end = array + endIndex; + array += startIndex; + while (array != end) { + if (*(array++) != 0) { + return true; + } + } + return false; +} + +static const int32_t GAMEPAD_KEYCODES[] = { + AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, // + AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, // + AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, // + AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, // + AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, // + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, // +}; + +status_t EventHub::registerFdForEpoll(int fd) { + // TODO(b/121395353) - consider adding EPOLLRDHUP + struct epoll_event eventItem = {}; + eventItem.events = EPOLLIN | EPOLLWAKEUP; + eventItem.data.fd = fd; + if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { + ALOGE("Could not add fd to epoll instance: %s", strerror(errno)); + return -errno; + } + return OK; +} + +status_t EventHub::unregisterFdFromEpoll(int fd) { + if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr)) { + ALOGW("Could not remove fd from epoll instance: %s", strerror(errno)); + return -errno; + } + return OK; +} + +status_t EventHub::registerDeviceForEpollLocked(Device* device) { + if (device == nullptr) { + if (DEBUG) { + LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device"); + } + return BAD_VALUE; + } + status_t result = registerFdForEpoll(device->fd); + if (result != OK) { + ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id); + return result; + } + if (device->videoDevice) { + registerVideoDeviceForEpollLocked(*device->videoDevice); + } + return result; +} + +void EventHub::registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) { + status_t result = registerFdForEpoll(videoDevice.getFd()); + if (result != OK) { + ALOGE("Could not add video device %s to epoll", videoDevice.getName().c_str()); + } +} + +status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) { + if (device->hasValidFd()) { + status_t result = unregisterFdFromEpoll(device->fd); + if (result != OK) { + ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id); + return result; + } + } + if (device->videoDevice) { + unregisterVideoDeviceFromEpollLocked(*device->videoDevice); + } + return OK; +} + +void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) { + if (videoDevice.hasValidFd()) { + status_t result = unregisterFdFromEpoll(videoDevice.getFd()); + if (result != OK) { + ALOGW("Could not remove video device fd from epoll for device: %s", + videoDevice.getName().c_str()); + } + } +} + +status_t EventHub::openDeviceLocked(const char* devicePath) { + char buffer[80]; + + ALOGV("Opening device: %s", devicePath); + + int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK); + if (fd < 0) { + ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); + return -1; + } + + InputDeviceIdentifier identifier; + + // Get device name. + if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { + ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.name = buffer; + } + + // Check to see if the device is on our excluded list + for (size_t i = 0; i < mExcludedDevices.size(); i++) { + const std::string& item = mExcludedDevices[i]; + if (identifier.name == item) { + ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str()); + close(fd); + return -1; + } + } + + // Get device driver version. + int driverVersion; + if (ioctl(fd, EVIOCGVERSION, &driverVersion)) { + ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); + close(fd); + return -1; + } + + // Get device identifier. + struct input_id inputId; + if (ioctl(fd, EVIOCGID, &inputId)) { + ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); + close(fd); + return -1; + } + identifier.bus = inputId.bustype; + identifier.product = inputId.product; + identifier.vendor = inputId.vendor; + identifier.version = inputId.version; + + // Get device physical location. + if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { + // fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.location = buffer; + } + + // Get device unique id. + if (ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { + // fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); + } else { + buffer[sizeof(buffer) - 1] = '\0'; + identifier.uniqueId = buffer; + } + + // Fill in the descriptor. + assignDescriptorLocked(identifier); + + // Allocate device. (The device object takes ownership of the fd at this point.) + int32_t deviceId = mNextDeviceId++; + Device* device = new Device(fd, deviceId, devicePath, identifier); + + ALOGV("add device %d: %s\n", deviceId, devicePath); + ALOGV(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + identifier.bus, identifier.vendor, identifier.product, identifier.version); + ALOGV(" name: \"%s\"\n", identifier.name.c_str()); + ALOGV(" location: \"%s\"\n", identifier.location.c_str()); + ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str()); + ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str()); + ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, + driverVersion & 0xff); + + // Load the configuration file for the device. + loadConfigurationLocked(device); + + // Figure out the kinds of events the device reports. + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); + ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); + ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); + ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask); + ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); + + // See if this is a keyboard. Ignore everything in the button range except for + // joystick and gamepad buttons which are handled like keyboards for the most part. + bool haveKeyboardKeys = + containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || + containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), + sizeof_bit_array(KEY_MAX + 1)); + bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), + sizeof_bit_array(BTN_MOUSE)) || + containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), + sizeof_bit_array(BTN_DIGI)); + if (haveKeyboardKeys || haveGamepadButtons) { + device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + } + + // See if this is a cursor device such as a trackball or mouse. + if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) && + test_bit(REL_Y, device->relBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_CURSOR; + } + + // See if this is a rotary encoder type device. + String8 deviceType = String8(); + if (device->configuration && + device->configuration->tryGetProperty(String8("device.type"), deviceType)) { + if (!deviceType.compare(String8("rotaryEncoder"))) { + device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; + } + } + + // See if this is a touch pad. + // Is this a new modern multi-touch driver? + if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && + test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { + // Some joysticks such as the PS3 controller report axes that conflict + // with the ABS_MT range. Try to confirm that the device really is + // a touch screen. + if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { + device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; + } + // Is this an old style single-touch driver? + } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) && + test_bit(ABS_Y, device->absBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_TOUCH; + // Is this a BT stylus? + } else if ((test_bit(ABS_PRESSURE, device->absBitmask) || + test_bit(BTN_TOUCH, device->keyBitmask)) && + !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; + // Keyboard will try to claim some of the buttons but we really want to reserve those so we + // can fuse it with the touch screen data, so just take them back. Note this means an + // external stylus cannot also be a keyboard device. + device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD; + } + + // See if this device is a joystick. + // Assumes that joysticks always have gamepad buttons in order to distinguish them + // from other devices such as accelerometers that also have absolute axes. + if (haveGamepadButtons) { + uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK; + for (int i = 0; i <= ABS_MAX; i++) { + if (test_bit(i, device->absBitmask) && + (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { + device->classes = assumedClasses; + break; + } + } + } + + // Check whether this device has switches. + for (int i = 0; i <= SW_MAX; i++) { + if (test_bit(i, device->swBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_SWITCH; + break; + } + } + + // Check whether this device supports the vibrator. + if (test_bit(FF_RUMBLE, device->ffBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_VIBRATOR; + } + + // Configure virtual keys. + if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { + // Load the virtual keys for the touch screen, if any. + // We do this now so that we can make sure to load the keymap if necessary. + bool success = loadVirtualKeyMapLocked(device); + if (success) { + device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + } + } + + // Load the key map. + // We need to do this for joysticks too because the key layout may specify axes. + status_t keyMapStatus = NAME_NOT_FOUND; + if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { + // Load the keymap for the device. + keyMapStatus = loadKeyMapLocked(device); + } + + // Configure the keyboard, gamepad or virtual keyboard. + if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { + // Register the keyboard as a built-in keyboard if it is eligible. + if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD && + isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { + mBuiltInKeyboardId = device->id; + } + + // 'Q' key support = cheap test of whether this is an alpha-capable kbd + if (hasKeycodeLocked(device, AKEYCODE_Q)) { + device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; + } + + // See if this device has a DPAD. + if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && + hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && + hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && + hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && + hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { + device->classes |= INPUT_DEVICE_CLASS_DPAD; + } + + // See if this device has a gamepad. + for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) { + if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { + device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; + break; + } + } + } + + // If the device isn't recognized as something we handle, don't monitor it. + if (device->classes == 0) { + ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath, + device->identifier.name.c_str()); + delete device; + return -1; + } + + // Determine whether the device has a mic. + if (deviceHasMicLocked(device)) { + device->classes |= INPUT_DEVICE_CLASS_MIC; + } + + // Determine whether the device is external or internal. + if (isExternalDeviceLocked(device)) { + device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; + } + + if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) && + device->classes & INPUT_DEVICE_CLASS_GAMEPAD) { + device->controllerNumber = getNextControllerNumberLocked(device); + setLedForControllerLocked(device); + } + + // Find a matching video device by comparing device names + // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll + for (std::unique_ptr& videoDevice : mUnattachedVideoDevices) { + if (device->identifier.name == videoDevice->getName()) { + device->videoDevice = std::move(videoDevice); + break; + } + } + mUnattachedVideoDevices + .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(), + [](const std::unique_ptr& videoDevice) { + return videoDevice == nullptr; + }), + mUnattachedVideoDevices.end()); + + if (registerDeviceForEpollLocked(device) != OK) { + delete device; + return -1; + } + + configureFd(device); + + ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " + "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", + deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes, + device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(), + device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId)); + + addDeviceLocked(device); + return OK; +} + +void EventHub::configureFd(Device* device) { + // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type + if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { + // Disable kernel key repeat since we handle it ourselves + unsigned int repeatRate[] = {0, 0}; + if (ioctl(device->fd, EVIOCSREP, repeatRate)) { + ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(), + strerror(errno)); + } + } + + // Tell the kernel that we want to use the monotonic clock for reporting timestamps + // associated with input events. This is important because the input system + // uses the timestamps extensively and assumes they were recorded using the monotonic + // clock. + int clockId = CLOCK_MONOTONIC; + bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); + ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); +} + +void EventHub::openVideoDeviceLocked(const std::string& devicePath) { + std::unique_ptr videoDevice = TouchVideoDevice::create(devicePath); + if (!videoDevice) { + ALOGE("Could not create touch video device for %s. Ignoring", devicePath.c_str()); + return; + } + // Transfer ownership of this video device to a matching input device + for (size_t i = 0; i < mDevices.size(); i++) { + Device* device = mDevices.valueAt(i); + if (videoDevice->getName() == device->identifier.name) { + device->videoDevice = std::move(videoDevice); + if (device->enabled) { + registerVideoDeviceForEpollLocked(*device->videoDevice); + } + return; + } + } + + // Couldn't find a matching input device, so just add it to a temporary holding queue. + // A matching input device may appear later. + ALOGI("Adding video device %s to list of unattached video devices", + videoDevice->getName().c_str()); + mUnattachedVideoDevices.push_back(std::move(videoDevice)); +} + +bool EventHub::isDeviceEnabled(int32_t deviceId) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); + return false; + } + return device->enabled; +} + +status_t EventHub::enableDevice(int32_t deviceId) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); + return BAD_VALUE; + } + if (device->enabled) { + ALOGW("Duplicate call to %s, input device %" PRId32 " already enabled", __func__, deviceId); + return OK; + } + status_t result = device->enable(); + if (result != OK) { + ALOGE("Failed to enable device %" PRId32, deviceId); + return result; + } + + configureFd(device); + + return registerDeviceForEpollLocked(device); +} + +status_t EventHub::disableDevice(int32_t deviceId) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); + return BAD_VALUE; + } + if (!device->enabled) { + ALOGW("Duplicate call to %s, input device already disabled", __func__); + return OK; + } + unregisterDeviceFromEpollLocked(device); + return device->disable(); +} + +void EventHub::createVirtualKeyboardLocked() { + InputDeviceIdentifier identifier; + identifier.name = "Virtual"; + identifier.uniqueId = ""; + assignDescriptorLocked(identifier); + + Device* device = + new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "", identifier); + device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | + INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL; + loadKeyMapLocked(device); + addDeviceLocked(device); +} + +void EventHub::addDeviceLocked(Device* device) { + mDevices.add(device->id, device); + device->next = mOpeningDevices; + mOpeningDevices = device; +} + +void EventHub::loadConfigurationLocked(Device* device) { + device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( + device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); + if (device->configurationFile.empty()) { + ALOGD("No input device configuration file found for device '%s'.", + device->identifier.name.c_str()); + } else { + status_t status = PropertyMap::load(String8(device->configurationFile.c_str()), + &device->configuration); + if (status) { + ALOGE("Error loading input device configuration file for device '%s'. " + "Using default configuration.", + device->identifier.name.c_str()); + } + } +} + +bool EventHub::loadVirtualKeyMapLocked(Device* device) { + // The virtual key map is supplied by the kernel as a system board property file. + std::string path; + path += "/sys/board_properties/virtualkeys."; + path += device->identifier.getCanonicalName(); + if (access(path.c_str(), R_OK)) { + return false; + } + device->virtualKeyMap = VirtualKeyMap::load(path); + return device->virtualKeyMap != nullptr; +} + +status_t EventHub::loadKeyMapLocked(Device* device) { + return device->keyMap.load(device->identifier, device->configuration); +} + +bool EventHub::isExternalDeviceLocked(Device* device) { + if (device->configuration) { + bool value; + if (device->configuration->tryGetProperty(String8("device.internal"), value)) { + return !value; + } + } + return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH; +} + +bool EventHub::deviceHasMicLocked(Device* device) { + if (device->configuration) { + bool value; + if (device->configuration->tryGetProperty(String8("audio.mic"), value)) { + return value; + } + } + return false; +} + +int32_t EventHub::getNextControllerNumberLocked(Device* device) { + if (mControllerNumbers.isFull()) { + ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s", + device->identifier.name.c_str()); + return 0; + } + // Since the controller number 0 is reserved for non-controllers, translate all numbers up by + // one + return static_cast(mControllerNumbers.markFirstUnmarkedBit() + 1); +} + +void EventHub::releaseControllerNumberLocked(Device* device) { + int32_t num = device->controllerNumber; + device->controllerNumber = 0; + if (num == 0) { + return; + } + mControllerNumbers.clearBit(static_cast(num - 1)); +} + +void EventHub::setLedForControllerLocked(Device* device) { + for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) { + setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1); + } +} + +bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { + if (!device->keyMap.haveKeyLayout()) { + return false; + } + + std::vector scanCodes; + device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + const size_t N = scanCodes.size(); + for (size_t i = 0; i < N && i <= KEY_MAX; i++) { + int32_t sc = scanCodes[i]; + if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { + return true; + } + } + + return false; +} + +status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const { + if (!device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; + } + + int32_t scanCode; + if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { + if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { + *outScanCode = scanCode; + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +void EventHub::closeDeviceByPathLocked(const char* devicePath) { + Device* device = getDeviceByPathLocked(devicePath); + if (device) { + closeDeviceLocked(device); + return; + } + ALOGV("Remove device: %s not found, device may already have been removed.", devicePath); +} + +/** + * Find the video device by filename, and close it. + * The video device is closed by path during an inotify event, where we don't have the + * additional context about the video device fd, or the associated input device. + */ +void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) { + // A video device may be owned by an existing input device, or it may be stored in + // the mUnattachedVideoDevices queue. Check both locations. + for (size_t i = 0; i < mDevices.size(); i++) { + Device* device = mDevices.valueAt(i); + if (device->videoDevice && device->videoDevice->getPath() == devicePath) { + unregisterVideoDeviceFromEpollLocked(*device->videoDevice); + device->videoDevice = nullptr; + return; + } + } + mUnattachedVideoDevices + .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(), + [&devicePath]( + const std::unique_ptr& videoDevice) { + return videoDevice->getPath() == devicePath; + }), + mUnattachedVideoDevices.end()); +} + +void EventHub::closeAllDevicesLocked() { + mUnattachedVideoDevices.clear(); + while (mDevices.size() > 0) { + closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); + } +} + +void EventHub::closeDeviceLocked(Device* device) { + ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(), + device->identifier.name.c_str(), device->id, device->fd, device->classes); + + if (device->id == mBuiltInKeyboardId) { + ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", + device->path.c_str(), mBuiltInKeyboardId); + mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; + } + + unregisterDeviceFromEpollLocked(device); + if (device->videoDevice) { + // This must be done after the video device is removed from epoll + mUnattachedVideoDevices.push_back(std::move(device->videoDevice)); + } + + releaseControllerNumberLocked(device); + + mDevices.removeItem(device->id); + device->close(); + + // Unlink for opening devices list if it is present. + Device* pred = nullptr; + bool found = false; + for (Device* entry = mOpeningDevices; entry != nullptr;) { + if (entry == device) { + found = true; + break; + } + pred = entry; + entry = entry->next; + } + if (found) { + // Unlink the device from the opening devices list then delete it. + // We don't need to tell the client that the device was closed because + // it does not even know it was opened in the first place. + ALOGI("Device %s was immediately closed after opening.", device->path.c_str()); + if (pred) { + pred->next = device->next; + } else { + mOpeningDevices = device->next; + } + delete device; + } else { + // Link into closing devices list. + // The device will be deleted later after we have informed the client. + device->next = mClosingDevices; + mClosingDevices = device; + } +} + +status_t EventHub::readNotifyLocked() { + int res; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event* event; + + ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); + res = read(mINotifyFd, event_buf, sizeof(event_buf)); + if (res < (int)sizeof(*event)) { + if (errno == EINTR) return 0; + ALOGW("could not get event, %s\n", strerror(errno)); + return -1; + } + + while (res >= (int)sizeof(*event)) { + event = (struct inotify_event*)(event_buf + event_pos); + if (event->len) { + if (event->wd == mInputWd) { + std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name); + if (event->mask & IN_CREATE) { + openDeviceLocked(filename.c_str()); + } else { + ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); + closeDeviceByPathLocked(filename.c_str()); + } + } else if (event->wd == mVideoWd) { + if (isV4lTouchNode(event->name)) { + std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name); + if (event->mask & IN_CREATE) { + openVideoDeviceLocked(filename); + } else { + ALOGI("Removing video device '%s' due to inotify event", filename.c_str()); + closeVideoDeviceByPathLocked(filename); + } + } + } else { + LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +status_t EventHub::scanDirLocked(const char* dirname) { + char devname[PATH_MAX]; + char* filename; + DIR* dir; + struct dirent* de; + dir = opendir(dirname); + if (dir == nullptr) return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while ((de = readdir(dir))) { + if (de->d_name[0] == '.' && + (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + openDeviceLocked(devname); + } + closedir(dir); + return 0; +} + +/** + * Look for all dirname/v4l-touch* devices, and open them. + */ +status_t EventHub::scanVideoDirLocked(const std::string& dirname) { + DIR* dir; + struct dirent* de; + dir = opendir(dirname.c_str()); + if (!dir) { + ALOGE("Could not open video directory %s", dirname.c_str()); + return BAD_VALUE; + } + + while ((de = readdir(dir))) { + const char* name = de->d_name; + if (isV4lTouchNode(name)) { + ALOGI("Found touch video device %s", name); + openVideoDeviceLocked(dirname + "/" + name); + } + } + closedir(dir); + return OK; +} + +void EventHub::requestReopenDevices() { + ALOGV("requestReopenDevices() called"); + + AutoMutex _l(mLock); + mNeedToReopenDevices = true; +} + +void EventHub::dump(std::string& dump) { + dump += "Event Hub State:\n"; + + { // acquire lock + AutoMutex _l(mLock); + + dump += StringPrintf(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId); + + dump += INDENT "Devices:\n"; + + for (size_t i = 0; i < mDevices.size(); i++) { + const Device* device = mDevices.valueAt(i); + if (mBuiltInKeyboardId == device->id) { + dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", + device->id, device->identifier.name.c_str()); + } else { + dump += StringPrintf(INDENT2 "%d: %s\n", device->id, + device->identifier.name.c_str()); + } + dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes); + dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str()); + dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled)); + dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str()); + dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.c_str()); + dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber); + dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str()); + dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " + "product=0x%04x, version=0x%04x\n", + device->identifier.bus, device->identifier.vendor, + device->identifier.product, device->identifier.version); + dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n", + device->keyMap.keyLayoutFile.c_str()); + dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", + device->keyMap.keyCharacterMapFile.c_str()); + dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", + device->configurationFile.c_str()); + dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", + toString(device->overlayKeyMap != nullptr)); + dump += INDENT3 "VideoDevice: "; + if (device->videoDevice) { + dump += device->videoDevice->dump() + "\n"; + } else { + dump += "\n"; + } + } + + dump += INDENT "Unattached video devices:\n"; + for (const std::unique_ptr& videoDevice : mUnattachedVideoDevices) { + dump += INDENT2 + videoDevice->dump() + "\n"; + } + if (mUnattachedVideoDevices.empty()) { + dump += INDENT2 "\n"; + } + } // release lock +} + +void EventHub::monitor() { + // Acquire and release the lock to ensure that the event hub has not deadlocked. + mLock.lock(); + mLock.unlock(); +} + +}; // namespace android diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp new file mode 100644 index 0000000000..521f621dea --- /dev/null +++ b/services/inputflinger/reader/InputReader.cpp @@ -0,0 +1,7461 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputReader" + +//#define LOG_NDEBUG 0 + +// Log debug messages for each raw event received from the EventHub. +#define DEBUG_RAW_EVENTS 0 + +// Log debug messages about touch screen filtering hacks. +#define DEBUG_HACKS 0 + +// Log debug messages about virtual key processing. +#define DEBUG_VIRTUAL_KEYS 0 + +// Log debug messages about pointers. +#define DEBUG_POINTERS 0 + +// Log debug messages about pointer assignment calculations. +#define DEBUG_POINTER_ASSIGNMENT 0 + +// Log debug messages about gesture detection. +#define DEBUG_GESTURES 0 + +// Log debug messages about the vibrator. +#define DEBUG_VIBRATOR 0 + +// Log debug messages about fusing stylus data. +#define DEBUG_STYLUS_FUSION 0 + +#include "InputReader.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define INDENT " " +#define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " +#define INDENT5 " " + +using android::base::StringPrintf; + +namespace android { + +// --- Constants --- + +// Maximum number of slots supported when using the slot-based Multitouch Protocol B. +static constexpr size_t MAX_SLOTS = 32; + +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + +// Artificial latency on synthetic events created from stylus data without corresponding touch +// data. +static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); + +// --- Static Functions --- + +template +inline static T abs(const T& value) { + return value < 0 ? -value : value; +} + +template +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; +} + +inline static float avg(float x, float y) { + return (x + y) / 2; +} + +inline static float distance(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); +} + +inline static int32_t signExtendNybble(int32_t value) { + return value >= 8 ? value - 16 : value; +} + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, + const int32_t map[][4], size_t mapSize) { + if (orientation != DISPLAY_ORIENTATION_0) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][orientation]; + } + } + } + return value; +} + +static const int32_t keyCodeRotationMap[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, +}; +static const size_t keyCodeRotationMapSize = + sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); + +static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], + size_t mapSize) { + if (orientation == DISPLAY_ORIENTATION_180) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][1]; + } + } + } + return value; +} + +// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X +static int32_t stemKeyRotationMap[][2] = { + // key codes enumerated with the original (unrotated) key first + // no rotation, 180 degree rotation + {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, + {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, + {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, + {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, +}; +static const size_t stemKeyRotationMapSize = + sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); + +static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); + return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, + keyCodeRotationMapSize); +} + +static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { + float temp; + switch (orientation) { + case DISPLAY_ORIENTATION_90: + temp = *deltaX; + *deltaX = *deltaY; + *deltaY = -temp; + break; + + case DISPLAY_ORIENTATION_180: + *deltaX = -*deltaX; + *deltaY = -*deltaY; + break; + + case DISPLAY_ORIENTATION_270: + temp = *deltaX; + *deltaX = -*deltaY; + *deltaY = temp; + break; + } +} + +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0; +} + +// Returns true if the pointer should be reported as being down given the specified +// button states. This determines whether the event is reported as a touch event. +static bool isPointerDown(int32_t buttonState) { + return buttonState & + (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY); +} + +static float calculateCommonVector(float a, float b) { + if (a > 0 && b > 0) { + return a < b ? a : b; + } else if (a < 0 && b < 0) { + return a > b ? a : b; + } else { + return 0; + } +} + +static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { + if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && + (currentButtonState & buttonState)) || + (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && + !(currentButtonState & buttonState))) { + NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); + context->getListener()->notifyKey(&args); + } +} + +static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState) { + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, + AKEYCODE_BACK); + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD); +} + +// --- InputReader --- + +InputReader::InputReader(std::shared_ptr eventHub, + const sp& policy, + const sp& listener) + : mContext(this), + mEventHub(eventHub), + mPolicy(policy), + mNextSequenceNum(1), + mGlobalMetaState(0), + mGeneration(1), + mDisableVirtualKeysTimeout(LLONG_MIN), + mNextTimeout(LLONG_MAX), + mConfigurationChangesToRefresh(0) { + mQueuedListener = new QueuedInputListener(listener); + + { // acquire lock + AutoMutex _l(mLock); + + refreshConfigurationLocked(0); + updateGlobalMetaStateLocked(); + } // release lock +} + +InputReader::~InputReader() { + for (size_t i = 0; i < mDevices.size(); i++) { + delete mDevices.valueAt(i); + } +} + +void InputReader::loopOnce() { + int32_t oldGeneration; + int32_t timeoutMillis; + bool inputDevicesChanged = false; + std::vector inputDevices; + { // acquire lock + AutoMutex _l(mLock); + + oldGeneration = mGeneration; + timeoutMillis = -1; + + uint32_t changes = mConfigurationChangesToRefresh; + if (changes) { + mConfigurationChangesToRefresh = 0; + timeoutMillis = 0; + refreshConfigurationLocked(changes); + } else if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); + } + } // release lock + + size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + + { // acquire lock + AutoMutex _l(mLock); + mReaderIsAliveCondition.broadcast(); + + if (count) { + processEventsLocked(mEventBuffer, count); + } + + if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now >= mNextTimeout) { +#if DEBUG_RAW_EVENTS + ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); +#endif + mNextTimeout = LLONG_MAX; + timeoutExpiredLocked(now); + } + } + + if (oldGeneration != mGeneration) { + inputDevicesChanged = true; + getInputDevicesLocked(inputDevices); + } + } // release lock + + // Send out a message that the describes the changed input devices. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + + // Flush queued events out to the listener. + // This must happen outside of the lock because the listener could potentially call + // back into the InputReader's methods, such as getScanCodeState, or become blocked + // on another thread similarly waiting to acquire the InputReader lock thereby + // resulting in a deadlock. This situation is actually quite plausible because the + // listener is actually the input dispatcher, which calls into the window manager, + // which occasionally calls into the input reader. + mQueuedListener->flush(); +} + +void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { + for (const RawEvent* rawEvent = rawEvents; count;) { + int32_t type = rawEvent->type; + size_t batchSize = 1; + if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { + int32_t deviceId = rawEvent->deviceId; + while (batchSize < count) { + if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT || + rawEvent[batchSize].deviceId != deviceId) { + break; + } + batchSize += 1; + } +#if DEBUG_RAW_EVENTS + ALOGD("BatchSize: %zu Count: %zu", batchSize, count); +#endif + processEventsForDeviceLocked(deviceId, rawEvent, batchSize); + } else { + switch (rawEvent->type) { + case EventHubInterface::DEVICE_ADDED: + addDeviceLocked(rawEvent->when, rawEvent->deviceId); + break; + case EventHubInterface::DEVICE_REMOVED: + removeDeviceLocked(rawEvent->when, rawEvent->deviceId); + break; + case EventHubInterface::FINISHED_DEVICE_SCAN: + handleConfigurationChangedLocked(rawEvent->when); + break; + default: + ALOG_ASSERT(false); // can't happen + break; + } + } + count -= batchSize; + rawEvent += batchSize; + } +} + +void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + return; + } + + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); + uint32_t classes = mEventHub->getDeviceClasses(deviceId); + int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); + + InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); + device->configure(when, &mConfig, 0); + device->reset(when); + + if (device->isIgnored()) { + ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, + identifier.name.c_str()); + } else { + ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(), + device->getSources()); + } + + mDevices.add(deviceId, device); + bumpGenerationLocked(); + + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + notifyExternalStylusPresenceChanged(); + } +} + +void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { + InputDevice* device = nullptr; + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); + return; + } + + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + bumpGenerationLocked(); + + if (device->isIgnored()) { + ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), + device->getName().c_str()); + } else { + ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(), + device->getName().c_str(), device->getSources()); + } + + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + notifyExternalStylusPresenceChanged(); + } + + device->reset(when); + delete device; +} + +InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes) { + InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), + controllerNumber, identifier, classes); + + // External devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { + device->setExternal(true); + } + + // Devices with mics. + if (classes & INPUT_DEVICE_CLASS_MIC) { + device->setMic(true); + } + + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + device->addMapper(new SwitchInputMapper(device)); + } + + // Scroll wheel-like devices. + if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { + device->addMapper(new RotaryEncoderInputMapper(device)); + } + + // Vibrator-like devices. + if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { + device->addMapper(new VibratorInputMapper(device)); + } + + // Keyboard-like devices. + uint32_t keyboardSource = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSource |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSource |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSource |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSource != 0) { + device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); + } + + // Cursor-like devices. + if (classes & INPUT_DEVICE_CLASS_CURSOR) { + device->addMapper(new CursorInputMapper(device)); + } + + // Touchscreens and touchpad devices. + if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { + device->addMapper(new MultiTouchInputMapper(device)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { + device->addMapper(new SingleTouchInputMapper(device)); + } + + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + device->addMapper(new JoystickInputMapper(device)); + } + + // External stylus-like devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + device->addMapper(new ExternalStylusInputMapper(device)); + } + + return device; +} + +void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, + size_t count) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Discarding event for unknown deviceId %d.", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + // ALOGD("Discarding event for ignored deviceId %d.", deviceId); + return; + } + + device->process(rawEvents, count); +} + +void InputReader::timeoutExpiredLocked(nsecs_t when) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored()) { + device->timeoutExpired(when); + } + } +} + +void InputReader::handleConfigurationChangedLocked(nsecs_t when) { + // Reset global meta state because it depends on the list of all configured devices. + updateGlobalMetaStateLocked(); + + // Enqueue configuration changed. + NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); + mQueuedListener->notifyConfigurationChanged(&args); +} + +void InputReader::refreshConfigurationLocked(uint32_t changes) { + mPolicy->getReaderConfiguration(&mConfig); + mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); + + if (changes) { + ALOGI("Reconfiguring input devices, changes=%s", + InputReaderConfiguration::changesToString(changes).c_str()); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { + mEventHub->requestReopenDevices(); + } else { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->configure(now, &mConfig, changes); + } + } + } +} + +void InputReader::updateGlobalMetaStateLocked() { + mGlobalMetaState = 0; + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + mGlobalMetaState |= device->getMetaState(); + } +} + +int32_t InputReader::getGlobalMetaStateLocked() { + return mGlobalMetaState; +} + +void InputReader::notifyExternalStylusPresenceChanged() { + refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); +} + +void InputReader::getExternalStylusDevicesLocked(std::vector& outDevices) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + outDevices.push_back(info); + } + } +} + +void InputReader::dispatchExternalStylusState(const StylusState& state) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->updateExternalStylusState(state); + } +} + +void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { + mDisableVirtualKeysTimeout = time; +} + +bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode) { + if (now < mDisableVirtualKeysTimeout) { + ALOGI("Dropping virtual key from device %s because virtual keys are " + "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", + device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, + scanCode); + return true; + } else { + return false; + } +} + +void InputReader::fadePointerLocked() { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->fadePointer(); + } +} + +void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { + if (when < mNextTimeout) { + mNextTimeout = when; + mEventHub->wake(); + } +} + +int32_t InputReader::bumpGenerationLocked() { + return ++mGeneration; +} + +void InputReader::getInputDevices(std::vector& outInputDevices) { + AutoMutex _l(mLock); + getInputDevicesLocked(outInputDevices); +} + +void InputReader::getInputDevicesLocked(std::vector& outInputDevices) { + outInputDevices.clear(); + + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored()) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + outInputDevices.push_back(info); + } + } +} + +int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState); +} + +int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState); +} + +int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState); +} + +int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. + int32_t currentResult = (device->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } + } + } + } + return result; +} + +void InputReader::toggleCapsLockState(int32_t deviceId) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + return; + } + + device->updateMetaState(AKEYCODE_CAPS_LOCK); +} + +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + AutoMutex _l(mLock); + + memset(outFlags, 0, numCodes); + return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} + +bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) { + bool result = false; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + } + return result; +} + +void InputReader::requestRefreshConfiguration(uint32_t changes) { + AutoMutex _l(mLock); + + if (changes) { + bool needWake = !mConfigurationChangesToRefresh; + mConfigurationChangesToRefresh |= changes; + + if (needWake) { + mEventHub->wake(); + } + } +} + +void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->cancelVibrate(token); + } +} + +bool InputReader::isInputDeviceEnabled(int32_t deviceId) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + return device->isEnabled(); + } + ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); + return false; +} + +bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); + return false; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (!device->isEnabled()) { + ALOGW("Ignoring disabled device %s", device->getName().c_str()); + return false; + } + + std::optional associatedDisplayId = device->getAssociatedDisplayId(); + // No associated display. By default, can dispatch to all displays. + if (!associatedDisplayId) { + return true; + } + + if (*associatedDisplayId == ADISPLAY_ID_NONE) { + ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str()); + return true; + } + + return *associatedDisplayId == displayId; +} + +void InputReader::dump(std::string& dump) { + AutoMutex _l(mLock); + + mEventHub->dump(dump); + dump += "\n"; + + dump += "Input Reader State:\n"; + + for (size_t i = 0; i < mDevices.size(); i++) { + mDevices.valueAt(i)->dump(dump); + } + + dump += INDENT "Configuration:\n"; + dump += INDENT2 "ExcludedDeviceNames: ["; + for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) { + if (i != 0) { + dump += ", "; + } + dump += mConfig.excludedDeviceNames[i]; + } + dump += "]\n"; + dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", + mConfig.virtualKeyQuietTime * 0.000001f); + + dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: " + "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + mConfig.pointerVelocityControlParameters.scale, + mConfig.pointerVelocityControlParameters.lowThreshold, + mConfig.pointerVelocityControlParameters.highThreshold, + mConfig.pointerVelocityControlParameters.acceleration); + + dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: " + "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + mConfig.wheelVelocityControlParameters.scale, + mConfig.wheelVelocityControlParameters.lowThreshold, + mConfig.wheelVelocityControlParameters.highThreshold, + mConfig.wheelVelocityControlParameters.acceleration); + + dump += StringPrintf(INDENT2 "PointerGesture:\n"); + dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(mConfig.pointerGesturesEnabled)); + dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n", + mConfig.pointerGestureQuietInterval * 0.000001f); + dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n", + mConfig.pointerGestureDragMinSwitchSpeed); + dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n", + mConfig.pointerGestureTapInterval * 0.000001f); + dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n", + mConfig.pointerGestureTapDragInterval * 0.000001f); + dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", mConfig.pointerGestureTapSlop); + dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n", + mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); + dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n", + mConfig.pointerGestureMultitouchMinDistance); + dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", + mConfig.pointerGestureSwipeTransitionAngleCosine); + dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", + mConfig.pointerGestureSwipeMaxWidthRatio); + dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n", + mConfig.pointerGestureMovementSpeedRatio); + dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", mConfig.pointerGestureZoomSpeedRatio); + + dump += INDENT3 "Viewports:\n"; + mConfig.dump(dump); +} + +void InputReader::monitor() { + // Acquire and release the lock to ensure that the reader has not deadlocked. + mLock.lock(); + mEventHub->wake(); + mReaderIsAliveCondition.wait(mLock); + mLock.unlock(); + + // Check the EventHub + mEventHub->monitor(); +} + +// --- InputReader::ContextImpl --- + +InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {} + +void InputReader::ContextImpl::updateGlobalMetaState() { + // lock is already held by the input loop + mReader->updateGlobalMetaStateLocked(); +} + +int32_t InputReader::ContextImpl::getGlobalMetaState() { + // lock is already held by the input loop + return mReader->getGlobalMetaStateLocked(); +} + +void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { + // lock is already held by the input loop + mReader->disableVirtualKeysUntilLocked(time); +} + +bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device, + int32_t keyCode, int32_t scanCode) { + // lock is already held by the input loop + return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode); +} + +void InputReader::ContextImpl::fadePointer() { + // lock is already held by the input loop + mReader->fadePointerLocked(); +} + +void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { + // lock is already held by the input loop + mReader->requestTimeoutAtTimeLocked(when); +} + +int32_t InputReader::ContextImpl::bumpGeneration() { + // lock is already held by the input loop + return mReader->bumpGenerationLocked(); +} + +void InputReader::ContextImpl::getExternalStylusDevices(std::vector& outDevices) { + // lock is already held by whatever called refreshConfigurationLocked + mReader->getExternalStylusDevicesLocked(outDevices); +} + +void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { + mReader->dispatchExternalStylusState(state); +} + +InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { + return mReader->mPolicy.get(); +} + +InputListenerInterface* InputReader::ContextImpl::getListener() { + return mReader->mQueuedListener.get(); +} + +EventHubInterface* InputReader::ContextImpl::getEventHub() { + return mReader->mEventHub.get(); +} + +uint32_t InputReader::ContextImpl::getNextSequenceNum() { + return (mReader->mNextSequenceNum)++; +} + +// --- InputDevice --- + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes) + : mContext(context), + mId(id), + mGeneration(generation), + mControllerNumber(controllerNumber), + mIdentifier(identifier), + mClasses(classes), + mSources(0), + mIsExternal(false), + mHasMic(false), + mDropUntilNextSync(false) {} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; + } + mMappers.clear(); +} + +bool InputDevice::isEnabled() { + return getEventHub()->isDeviceEnabled(mId); +} + +void InputDevice::setEnabled(bool enabled, nsecs_t when) { + if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { + ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " + "but the corresponding viewport is not found", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + + if (isEnabled() == enabled) { + return; + } + + if (enabled) { + getEventHub()->enableDevice(mId); + reset(when); + } else { + reset(when); + getEventHub()->disableDevice(mId); + } + // Must change generation to flag this device as changed + bumpGeneration(); +} + +void InputDevice::dump(std::string& dump) { + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + + dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), + deviceInfo.getDisplayName().c_str()); + dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); + dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); + dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); + if (mAssociatedDisplayPort) { + dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); + } else { + dump += "\n"; + } + dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); + dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); + dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); + + const std::vector& ranges = deviceInfo.getMotionRanges(); + if (!ranges.empty()) { + dump += INDENT2 "Motion Ranges:\n"; + for (size_t i = 0; i < ranges.size(); i++) { + const InputDeviceInfo::MotionRange& range = ranges[i]; + const char* label = getAxisLabel(range.axis); + char name[32]; + if (label) { + strncpy(name, label, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else { + snprintf(name, sizeof(name), "%d", range.axis); + } + dump += StringPrintf(INDENT3 + "%s: source=0x%08x, " + "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", + name, range.source, range.min, range.max, range.flat, range.fuzz, + range.resolution); + } + } + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->dump(dump); + } +} + +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.push_back(mapper); +} + +void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + mSources = 0; + + if (!isIgnored()) { + if (!changes) { // first time only + mContext->getEventHub()->getConfiguration(mId, &mConfiguration); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + sp keyboardLayout = + mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); + if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); + if (mAlias != alias) { + mAlias = alias; + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { + auto it = config->disabledDevices.find(mId); + bool enabled = it == config->disabledDevices.end(); + setEnabled(enabled, when); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + // In most situations, no port will be specified. + mAssociatedDisplayPort = std::nullopt; + mAssociatedViewport = std::nullopt; + // Find the display port that corresponds to the current input port. + const std::string& inputPort = mIdentifier.location; + if (!inputPort.empty()) { + const std::unordered_map& ports = config->portAssociations; + const auto& displayPort = ports.find(inputPort); + if (displayPort != ports.end()) { + mAssociatedDisplayPort = std::make_optional(displayPort->second); + } + } + + // If the device was explicitly disabled by the user, it would be present in the + // "disabledDevices" list. If it is associated with a specific display, and it was not + // explicitly disabled, then enable/disable the device based on whether we can find the + // corresponding viewport. + bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); + if (mAssociatedDisplayPort) { + mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); + if (!mAssociatedViewport) { + ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " + "but the corresponding viewport is not found.", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + } + + if (changes) { + // For first-time configuration, only allow device to be disabled after mappers have + // finished configuring. This is because we need to read some of the properties from + // the device's open fd. + setEnabled(enabled, when); + } + } + + for (InputMapper* mapper : mMappers) { + mapper->configure(when, config, changes); + mSources |= mapper->getSources(); + } + + // If a device is just plugged but it might be disabled, we need to update some info like + // axis range of touch from each InputMapper first, then disable it. + if (!changes) { + setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); + } + } +} + +void InputDevice::reset(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->reset(when); + } + + mContext->updateGlobalMetaState(); + + notifyReset(when); +} + +void InputDevice::process(const RawEvent* rawEvents, size_t count) { + // Process all of the events in order for each mapper. + // We cannot simply ask each mapper to process them in bulk because mappers may + // have side-effects that must be interleaved. For example, joystick movement events and + // gamepad button presses are handled by different mappers but they should be dispatched + // in the order received. + for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { +#if DEBUG_RAW_EVENTS + ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, + rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when); +#endif + + if (mDropUntilNextSync) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + mDropUntilNextSync = false; +#if DEBUG_RAW_EVENTS + ALOGD("Recovered from input event buffer overrun."); +#endif + } else { +#if DEBUG_RAW_EVENTS + ALOGD("Dropped input event while waiting for next input sync."); +#endif + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { + ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); + mDropUntilNextSync = true; + reset(rawEvent->when); + } else { + for (InputMapper* mapper : mMappers) { + mapper->process(rawEvent); + } + } + --count; + } +} + +void InputDevice::timeoutExpired(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->timeoutExpired(when); + } +} + +void InputDevice::updateExternalStylusState(const StylusState& state) { + for (InputMapper* mapper : mMappers) { + mapper->updateExternalStylusState(state); + } +} + +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, + mHasMic); + for (InputMapper* mapper : mMappers) { + mapper->populateDeviceInfo(outDeviceInfo); + } +} + +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState); +} + +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, &InputMapper::getScanCodeState); +} + +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, &InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } + } + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + return result; +} + +void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputDevice::cancelVibrate(int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->cancelVibrate(token); + } +} + +void InputDevice::cancelTouch(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->cancelTouch(when); + } +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + for (InputMapper* mapper : mMappers) { + result |= mapper->getMetaState(); + } + return result; +} + +void InputDevice::updateMetaState(int32_t keyCode) { + for (InputMapper* mapper : mMappers) { + mapper->updateMetaState(keyCode); + } +} + +void InputDevice::fadePointer() { + for (InputMapper* mapper : mMappers) { + mapper->fadePointer(); + } +} + +void InputDevice::bumpGeneration() { + mGeneration = mContext->bumpGeneration(); +} + +void InputDevice::notifyReset(nsecs_t when) { + NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); + mContext->getListener()->notifyDeviceReset(&args); +} + +std::optional InputDevice::getAssociatedDisplayId() { + // Check if we had associated to the specific display. + if (mAssociatedViewport) { + return mAssociatedViewport->displayId; + } + + // No associated display port, check if some InputMapper is associated. + for (InputMapper* mapper : mMappers) { + std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); + if (associatedDisplayId) { + return associatedDisplayId; + } + } + + return std::nullopt; +} + +// --- CursorButtonAccumulator --- + +CursorButtonAccumulator::CursorButtonAccumulator() { + clearButtons(); +} + +void CursorButtonAccumulator::reset(InputDevice* device) { + mBtnLeft = device->isKeyPressed(BTN_LEFT); + mBtnRight = device->isKeyPressed(BTN_RIGHT); + mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); + mBtnBack = device->isKeyPressed(BTN_BACK); + mBtnSide = device->isKeyPressed(BTN_SIDE); + mBtnForward = device->isKeyPressed(BTN_FORWARD); + mBtnExtra = device->isKeyPressed(BTN_EXTRA); + mBtnTask = device->isKeyPressed(BTN_TASK); +} + +void CursorButtonAccumulator::clearButtons() { + mBtnLeft = 0; + mBtnRight = 0; + mBtnMiddle = 0; + mBtnBack = 0; + mBtnSide = 0; + mBtnForward = 0; + mBtnExtra = 0; + mBtnTask = 0; +} + +void CursorButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_LEFT: + mBtnLeft = rawEvent->value; + break; + case BTN_RIGHT: + mBtnRight = rawEvent->value; + break; + case BTN_MIDDLE: + mBtnMiddle = rawEvent->value; + break; + case BTN_BACK: + mBtnBack = rawEvent->value; + break; + case BTN_SIDE: + mBtnSide = rawEvent->value; + break; + case BTN_FORWARD: + mBtnForward = rawEvent->value; + break; + case BTN_EXTRA: + mBtnExtra = rawEvent->value; + break; + case BTN_TASK: + mBtnTask = rawEvent->value; + break; + } + } +} + +uint32_t CursorButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnLeft) { + result |= AMOTION_EVENT_BUTTON_PRIMARY; + } + if (mBtnRight) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnMiddle) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + if (mBtnBack || mBtnSide) { + result |= AMOTION_EVENT_BUTTON_BACK; + } + if (mBtnForward || mBtnExtra) { + result |= AMOTION_EVENT_BUTTON_FORWARD; + } + return result; +} + +// --- CursorMotionAccumulator --- + +CursorMotionAccumulator::CursorMotionAccumulator() { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::clearRelativeAxes() { + mRelX = 0; + mRelY = 0; +} + +void CursorMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_X: + mRelX = rawEvent->value; + break; + case REL_Y: + mRelY = rawEvent->value; + break; + } + } +} + +void CursorMotionAccumulator::finishSync() { + clearRelativeAxes(); +} + +// --- CursorScrollAccumulator --- + +CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::configure(InputDevice* device) { + mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); + mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +} + +void CursorScrollAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::clearRelativeAxes() { + mRelWheel = 0; + mRelHWheel = 0; +} + +void CursorScrollAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_WHEEL: + mRelWheel = rawEvent->value; + break; + case REL_HWHEEL: + mRelHWheel = rawEvent->value; + break; + } + } +} + +void CursorScrollAccumulator::finishSync() { + clearRelativeAxes(); +} + +// --- TouchButtonAccumulator --- + +TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { + clearButtons(); +} + +void TouchButtonAccumulator::configure(InputDevice* device) { + mHaveBtnTouch = device->hasKey(BTN_TOUCH); + mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || + device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || + device->hasKey(BTN_TOOL_AIRBRUSH); +} + +void TouchButtonAccumulator::reset(InputDevice* device) { + mBtnTouch = device->isKeyPressed(BTN_TOUCH); + mBtnStylus = device->isKeyPressed(BTN_STYLUS); + // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); + mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); +} + +void TouchButtonAccumulator::clearButtons() { + mBtnTouch = 0; + mBtnStylus = 0; + mBtnStylus2 = 0; + mBtnToolFinger = 0; + mBtnToolPen = 0; + mBtnToolRubber = 0; + mBtnToolBrush = 0; + mBtnToolPencil = 0; + mBtnToolAirbrush = 0; + mBtnToolMouse = 0; + mBtnToolLens = 0; + mBtnToolDoubleTap = 0; + mBtnToolTripleTap = 0; + mBtnToolQuadTap = 0; +} + +void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_TOUCH: + mBtnTouch = rawEvent->value; + break; + case BTN_STYLUS: + mBtnStylus = rawEvent->value; + break; + case BTN_STYLUS2: + case BTN_0: // BTN_0 is what gets mapped for the HID usage + // Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = rawEvent->value; + break; + case BTN_TOOL_FINGER: + mBtnToolFinger = rawEvent->value; + break; + case BTN_TOOL_PEN: + mBtnToolPen = rawEvent->value; + break; + case BTN_TOOL_RUBBER: + mBtnToolRubber = rawEvent->value; + break; + case BTN_TOOL_BRUSH: + mBtnToolBrush = rawEvent->value; + break; + case BTN_TOOL_PENCIL: + mBtnToolPencil = rawEvent->value; + break; + case BTN_TOOL_AIRBRUSH: + mBtnToolAirbrush = rawEvent->value; + break; + case BTN_TOOL_MOUSE: + mBtnToolMouse = rawEvent->value; + break; + case BTN_TOOL_LENS: + mBtnToolLens = rawEvent->value; + break; + case BTN_TOOL_DOUBLETAP: + mBtnToolDoubleTap = rawEvent->value; + break; + case BTN_TOOL_TRIPLETAP: + mBtnToolTripleTap = rawEvent->value; + break; + case BTN_TOOL_QUADTAP: + mBtnToolQuadTap = rawEvent->value; + break; + } + } +} + +uint32_t TouchButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnStylus) { + result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + } + if (mBtnStylus2) { + result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } + return result; +} + +int32_t TouchButtonAccumulator::getToolType() const { + if (mBtnToolMouse || mBtnToolLens) { + return AMOTION_EVENT_TOOL_TYPE_MOUSE; + } + if (mBtnToolRubber) { + return AMOTION_EVENT_TOOL_TYPE_ERASER; + } + if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { + return AMOTION_EVENT_TOOL_TYPE_FINGER; + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +bool TouchButtonAccumulator::isToolActive() const { + return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || + mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || + mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; +} + +bool TouchButtonAccumulator::isHovering() const { + return mHaveBtnTouch && !mBtnTouch; +} + +bool TouchButtonAccumulator::hasStylus() const { + return mHaveStylus; +} + +// --- RawPointerAxes --- + +RawPointerAxes::RawPointerAxes() { + clear(); +} + +void RawPointerAxes::clear() { + x.clear(); + y.clear(); + pressure.clear(); + touchMajor.clear(); + touchMinor.clear(); + toolMajor.clear(); + toolMinor.clear(); + orientation.clear(); + distance.clear(); + tiltX.clear(); + tiltY.clear(); + trackingId.clear(); + slot.clear(); +} + +// --- RawPointerData --- + +RawPointerData::RawPointerData() { + clear(); +} + +void RawPointerData::clear() { + pointerCount = 0; + clearIdBits(); +} + +void RawPointerData::copyFrom(const RawPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + + int id = pointers[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { + float x = 0, y = 0; + uint32_t count = touchingIdBits.count(); + if (count) { + for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const Pointer& pointer = pointerForId(id); + x += pointer.x; + y += pointer.y; + } + x /= count; + y /= count; + } + *outX = x; + *outY = y; +} + +// --- CookedPointerData --- + +CookedPointerData::CookedPointerData() { + clear(); +} + +void CookedPointerData::clear() { + pointerCount = 0; + hoveringIdBits.clear(); + touchingIdBits.clear(); +} + +void CookedPointerData::copyFrom(const CookedPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(other.pointerProperties[i]); + pointerCoords[i].copyFrom(other.pointerCoords[i]); + + int id = pointerProperties[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +// --- SingleTouchMotionAccumulator --- + +SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { + clearAbsoluteAxes(); +} + +void SingleTouchMotionAccumulator::reset(InputDevice* device) { + mAbsX = device->getAbsoluteAxisValue(ABS_X); + mAbsY = device->getAbsoluteAxisValue(ABS_Y); + mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); + mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); + mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); + mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); + mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); +} + +void SingleTouchMotionAccumulator::clearAbsoluteAxes() { + mAbsX = 0; + mAbsY = 0; + mAbsPressure = 0; + mAbsToolWidth = 0; + mAbsDistance = 0; + mAbsTiltX = 0; + mAbsTiltY = 0; +} + +void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + switch (rawEvent->code) { + case ABS_X: + mAbsX = rawEvent->value; + break; + case ABS_Y: + mAbsY = rawEvent->value; + break; + case ABS_PRESSURE: + mAbsPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAbsToolWidth = rawEvent->value; + break; + case ABS_DISTANCE: + mAbsDistance = rawEvent->value; + break; + case ABS_TILT_X: + mAbsTiltX = rawEvent->value; + break; + case ABS_TILT_Y: + mAbsTiltY = rawEvent->value; + break; + } + } +} + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), + mSlots(nullptr), + mSlotCount(0), + mUsingSlotsProtocol(false), + mHaveStylus(false) {} + +MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { + delete[] mSlots; +} + +void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, + bool usingSlotsProtocol) { + mSlotCount = slotCount; + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + + delete[] mSlots; + mSlots = new Slot[slotCount]; +} + +void MultiTouchMotionAccumulator::reset(InputDevice* device) { + // Unfortunately there is no way to read the initial contents of the slots. + // So when we reset the accumulator, we must assume they are all zeroes. + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot + // before we start reading events from the device. It is possible that the + // current slot index will not be the same as it was when the first event was + // written into the evdev buffer, which means the input mapper could start + // out of sync with the initial state of the events in the evdev buffer. + // In the extremely unlikely case that this happens, the data from + // two slots will be confused until the next ABS_MT_SLOT event is received. + // This can cause the touch point to "jump", but at least there will be + // no stuck touches. + int32_t initialSlot; + status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, + &initialSlot); + if (status) { + ALOGD("Could not retrieve current multitouch slot index. status=%d", status); + initialSlot = -1; + } + clearSlots(initialSlot); + } else { + clearSlots(-1); + } +} + +void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { + if (mSlots) { + for (size_t i = 0; i < mSlotCount; i++) { + mSlots[i].clear(); + } + } + mCurrentSlot = initialSlot; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { +#if DEBUG_POINTERS + if (newSlot) { + ALOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } +#endif + } else { + Slot* slot = &mSlots[mCurrentSlot]; + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot->mInUse = true; + slot->mAbsMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + slot->mInUse = true; + slot->mAbsMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + slot->mInUse = true; + slot->mAbsMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot->mInUse = true; + slot->mAbsMTTouchMinor = rawEvent->value; + slot->mHaveAbsMTTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot->mInUse = true; + slot->mAbsMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot->mInUse = true; + slot->mAbsMTWidthMinor = rawEvent->value; + slot->mHaveAbsMTWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot->mInUse = true; + slot->mAbsMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot->mInUse = false; + } else { + slot->mInUse = true; + slot->mAbsMTTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot->mInUse = true; + slot->mAbsMTPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot->mInUse = true; + slot->mAbsMTDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->mInUse = true; + slot->mAbsMTToolType = rawEvent->value; + slot->mHaveAbsMTToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + clearSlots(-1); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +// --- MultiTouchMotionAccumulator::Slot --- + +MultiTouchMotionAccumulator::Slot::Slot() { + clear(); +} + +void MultiTouchMotionAccumulator::Slot::clear() { + mInUse = false; + mHaveAbsMTTouchMinor = false; + mHaveAbsMTWidthMinor = false; + mHaveAbsMTToolType = false; + mAbsMTPositionX = 0; + mAbsMTPositionY = 0; + mAbsMTTouchMajor = 0; + mAbsMTTouchMinor = 0; + mAbsMTWidthMajor = 0; + mAbsMTWidthMinor = 0; + mAbsMTOrientation = 0; + mAbsMTTrackingId = -1; + mAbsMTPressure = 0; + mAbsMTDistance = 0; + mAbsMTToolType = 0; +} + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMTToolType) { + switch (mAbsMTToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +// --- InputMapper --- + +InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} + +InputMapper::~InputMapper() {} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::dump(std::string& dump) {} + +void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) {} + +void InputMapper::reset(nsecs_t when) {} + +void InputMapper::timeoutExpired(nsecs_t when) {} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) {} + +void InputMapper::cancelVibrate(int32_t token) {} + +void InputMapper::cancelTouch(nsecs_t when) {} + +int32_t InputMapper::getMetaState() { + return 0; +} + +void InputMapper::updateMetaState(int32_t keyCode) {} + +void InputMapper::updateExternalStylusState(const StylusState& state) {} + +void InputMapper::fadePointer() {} + +status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { + return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); +} + +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + +void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name) { + if (axis.valid) { + dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, + axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); + } else { + dump += StringPrintf(INDENT4 "%s: unknown range\n", name); + } +} + +void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { + dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); + dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); + dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); +} + +// --- SwitchInputMapper --- + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) + : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} + +SwitchInputMapper::~SwitchInputMapper() {} + +uint32_t SwitchInputMapper::getSources() { + return AINPUT_SOURCE_SWITCH; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->code, rawEvent->value); + break; + + case EV_SYN: + if (rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } + } +} + +void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { + if (switchCode >= 0 && switchCode < 32) { + if (switchValue) { + mSwitchValues |= 1 << switchCode; + } else { + mSwitchValues &= ~(1 << switchCode); + } + mUpdatedSwitchMask |= 1 << switchCode; + } +} + +void SwitchInputMapper::sync(nsecs_t when) { + if (mUpdatedSwitchMask) { + uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; + NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, + mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + + mUpdatedSwitchMask = 0; + } +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + +void SwitchInputMapper::dump(std::string& dump) { + dump += INDENT2 "Switch Input Mapper:\n"; + dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); +} + +// --- VibratorInputMapper --- + +VibratorInputMapper::VibratorInputMapper(InputDevice* device) + : InputMapper(device), mVibrating(false) {} + +VibratorInputMapper::~VibratorInputMapper() {} + +uint32_t VibratorInputMapper::getSources() { + return 0; +} + +void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setVibrator(true); +} + +void VibratorInputMapper::process(const RawEvent* rawEvent) { + // TODO: Handle FF_STATUS, although it does not seem to be widely supported. +} + +void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +#if DEBUG_VIBRATOR + std::string patternStr; + for (size_t i = 0; i < patternSize; i++) { + if (i != 0) { + patternStr += ", "; + } + patternStr += StringPrintf("%" PRId64, pattern[i]); + } + ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), + patternStr.c_str(), repeat, token); +#endif + + mVibrating = true; + memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); + mPatternSize = patternSize; + mRepeat = repeat; + mToken = token; + mIndex = -1; + + nextStep(); +} + +void VibratorInputMapper::cancelVibrate(int32_t token) { +#if DEBUG_VIBRATOR + ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); +#endif + + if (mVibrating && mToken == token) { + stopVibrating(); + } +} + +void VibratorInputMapper::timeoutExpired(nsecs_t when) { + if (mVibrating) { + if (when >= mNextStepTime) { + nextStep(); + } else { + getContext()->requestTimeoutAtTime(mNextStepTime); + } + } +} + +void VibratorInputMapper::nextStep() { + mIndex += 1; + if (size_t(mIndex) >= mPatternSize) { + if (mRepeat < 0) { + // We are done. + stopVibrating(); + return; + } + mIndex = mRepeat; + } + + bool vibratorOn = mIndex & 1; + nsecs_t duration = mPattern[mIndex]; + if (vibratorOn) { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); +#endif + getEventHub()->vibrate(getDeviceId(), duration); + } else { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); + } + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mNextStepTime = now + duration; + getContext()->requestTimeoutAtTime(mNextStepTime); +#if DEBUG_VIBRATOR + ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); +#endif +} + +void VibratorInputMapper::stopVibrating() { + mVibrating = false; +#if DEBUG_VIBRATOR + ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); +} + +void VibratorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Vibrator Input Mapper:\n"; + dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); +} + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) + : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} + +KeyboardInputMapper::~KeyboardInputMapper() {} + +uint32_t KeyboardInputMapper::getSources() { + return mSource; +} + +int32_t KeyboardInputMapper::getOrientation() { + if (mViewport) { + return mViewport->orientation; + } + return DISPLAY_ORIENTATION_0; +} + +int32_t KeyboardInputMapper::getDisplayId() { + if (mViewport) { + return mViewport->displayId; + } + return ADISPLAY_ID_NONE; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); +} + +void KeyboardInputMapper::dump(std::string& dump) { + dump += INDENT2 "Keyboard Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); + dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); + dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +std::optional KeyboardInputMapper::findViewport( + nsecs_t when, const InputReaderConfiguration* config) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // No associated display defined, try to find default display if orientationAware. + if (mParameters.orientationAware) { + return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return std::nullopt; +} + +void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mViewport = findViewport(when, config); + } +} + +static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { + int32_t mapped = 0; + if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { + for (size_t i = 0; i < stemKeyRotationMapSize; i++) { + if (stemKeyRotationMap[i][0] == keyCode) { + stemKeyRotationMap[i][1] = mapped; + return; + } + } + } +} + +void KeyboardInputMapper::configureParameters() { + mParameters.orientationAware = false; + const PropertyMap& config = getDevice()->getConfiguration(); + config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); + + if (mParameters.orientationAware) { + mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); + mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); + mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); + mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); + } + + mParameters.handlesKeyRepeat = false; + config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); +} + +void KeyboardInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); + dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); +} + +void KeyboardInputMapper::reset(nsecs_t when) { + mMetaState = AMETA_NONE; + mDownTime = 0; + mKeyDowns.clear(); + mCurrentHidUsage = 0; + + resetLedState(); + + InputMapper::reset(when); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->code; + int32_t usageCode = mCurrentHidUsage; + mCurrentHidUsage = 0; + + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); + } + break; + } + case EV_MSC: { + if (rawEvent->code == MSC_SCAN) { + mCurrentHidUsage = rawEvent->value; + } + break; + } + case EV_SYN: { + if (rawEvent->code == SYN_REPORT) { + mCurrentHidUsage = 0; + } + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE || scanCode >= KEY_OK || + (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || + (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); +} + +bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + } + return false; +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { + int32_t keyCode; + int32_t keyMetaState; + uint32_t policyFlags; + + if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, + &keyMetaState, &policyFlags)) { + keyCode = AKEYCODE_UNKNOWN; + keyMetaState = mMetaState; + policyFlags = 0; + } + + if (down) { + // Rotate key codes according to orientation if needed. + if (mParameters.orientationAware) { + keyCode = rotateKeyCode(keyCode, getOrientation()); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + } else { + // key down + if ((policyFlags & POLICY_FLAG_VIRTUAL) && + mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { + return; + } + if (policyFlags & POLICY_FLAG_GESTURE) { + mDevice->cancelTouch(when); + } + + KeyDown keyDown; + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + mKeyDowns.push_back(keyDown); + } + + mDownTime = when; + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + } else { + // key was not actually down + ALOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().c_str(), keyCode, scanCode); + return; + } + } + + if (updateMetaStateIfNeeded(keyCode, down)) { + // If global meta state changed send it along with the key. + // If it has not changed then we'll use what keymap gave us, + // since key replacement logic might temporarily reset a few + // meta bits for given key. + keyMetaState = mMetaState; + } + + nsecs_t downTime = mDownTime; + + // Key down on external an keyboard should wake the device. + // We don't do this for internal keyboards to prevent them from waking up in your pocket. + // For internal keyboards, the key layout file should specify the policy flags for + // each wake key individually. + // TODO: Use the input device configuration to control this behavior more finely. + if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mParameters.handlesKeyRepeat) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + getListener()->notifyKey(&args); +} + +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} + +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} + +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} + +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + +void KeyboardInputMapper::updateMetaState(int32_t keyCode) { + updateMetaStateIfNeeded(keyCode, false); +} + +bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { + int32_t oldMetaState = mMetaState; + int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); + bool metaStateChanged = oldMetaState != newMetaState; + if (metaStateChanged) { + mMetaState = newMetaState; + updateLedState(false); + + getContext()->updateGlobalMetaState(); + } + + return metaStateChanged; +} + +void KeyboardInputMapper::resetLedState() { + initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); + initializeLedState(mNumLockLedState, ALED_NUM_LOCK); + initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); + + updateLedState(true); +} + +void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { + ledState.avail = getEventHub()->hasLed(getDeviceId(), led); + ledState.on = false; +} + +void KeyboardInputMapper::updateLedState(bool reset) { + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); + updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); + updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); +} + +void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, + int32_t modifier, bool reset) { + if (ledState.avail) { + bool desiredState = (mMetaState & modifier) != 0; + if (reset || ledState.on != desiredState) { + getEventHub()->setLedState(getDeviceId(), led, desiredState); + ledState.on = desiredState; + } + } +} + +std::optional KeyboardInputMapper::getAssociatedDisplayId() { + if (mViewport) { + return std::make_optional(mViewport->displayId); + } + return std::nullopt; +} + +// --- CursorInputMapper --- + +CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} + +CursorInputMapper::~CursorInputMapper() {} + +uint32_t CursorInputMapper::getSources() { + return mSource; +} + +void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mParameters.mode == Parameters::MODE_POINTER) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); + } + } else { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } +} + +void CursorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Cursor Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeVWheel())); + dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); + dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); + dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); + dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + mCursorScrollAccumulator.configure(getDevice()); + + // Configure basic parameters. + configureParameters(); + + // Configure device mode. + switch (mParameters.mode) { + case Parameters::MODE_POINTER_RELATIVE: + // Should not happen during first time configuration. + ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); + mParameters.mode = Parameters::MODE_POINTER; + [[fallthrough]]; + case Parameters::MODE_POINTER: + mSource = AINPUT_SOURCE_MOUSE; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + mXScale = 1.0f; + mYScale = 1.0f; + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + break; + case Parameters::MODE_NAVIGATION: + mSource = AINPUT_SOURCE_TRACKBALL; + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + break; + } + + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; + } + + if ((!changes && config->pointerCapture) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { + if (config->pointerCapture) { + if (mParameters.mode == Parameters::MODE_POINTER) { + mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + mSource = AINPUT_SOURCE_MOUSE_RELATIVE; + // Keep PointerController around in order to preserve the pointer position. + mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else { + ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); + } + } else { + if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { + mParameters.mode = Parameters::MODE_POINTER; + mSource = AINPUT_SOURCE_MOUSE; + } else { + ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); + } + } + bumpGeneration(); + if (changes) { + getDevice()->notifyReset(when); + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mOrientation = DISPLAY_ORIENTATION_0; + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } + } + + // Update the PointerController if viewports changed. + if (mParameters.mode == Parameters::MODE_POINTER) { + getPolicy()->obtainPointerController(getDeviceId()); + } + bumpGeneration(); + } +} + +void CursorInputMapper::configureParameters() { + mParameters.mode = Parameters::MODE_POINTER; + String8 cursorModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { + if (cursorModeString == "navigation") { + mParameters.mode = Parameters::MODE_NAVIGATION; + } else if (cursorModeString != "pointer" && cursorModeString != "default") { + ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); + } + } + + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + mParameters.hasAssociatedDisplay = true; + } +} + +void CursorInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); + + switch (mParameters.mode) { + case Parameters::MODE_POINTER: + dump += INDENT4 "Mode: pointer\n"; + break; + case Parameters::MODE_POINTER_RELATIVE: + dump += INDENT4 "Mode: relative pointer\n"; + break; + case Parameters::MODE_NAVIGATION: + dump += INDENT4 "Mode: navigation\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void CursorInputMapper::reset(nsecs_t when) { + mButtonState = 0; + mDownTime = 0; + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mCursorButtonAccumulator.reset(getDevice()); + mCursorMotionAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void CursorInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorMotionAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void CursorInputMapper::sync(nsecs_t when) { + int32_t lastButtonState = mButtonState; + int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); + mButtonState = currentButtonState; + + bool wasDown = isPointerDown(lastButtonState); + bool down = isPointerDown(currentButtonState); + bool downChanged; + if (!wasDown && down) { + mDownTime = when; + downChanged = true; + } else if (wasDown && !down) { + downChanged = true; + } else { + downChanged = false; + } + nsecs_t downTime = mDownTime; + bool buttonsChanged = currentButtonState != lastButtonState; + int32_t buttonsPressed = currentButtonState & ~lastButtonState; + int32_t buttonsReleased = lastButtonState & ~currentButtonState; + + float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; + float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; + bool moved = deltaX != 0 || deltaY != 0; + + // Rotate delta according to orientation if needed. + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && + (deltaX != 0.0f || deltaY != 0.0f)) { + rotateDelta(mOrientation, &deltaX, &deltaY); + } + + // Move the pointer. + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); + float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); + bool scrolled = vscroll != 0 || hscroll != 0; + + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mSource == AINPUT_SOURCE_MOUSE) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + + if (buttonsChanged) { + mPointerController->setButtonState(currentButtonState); + } + + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); + displayId = mPointerController->getDisplayId(); + } else { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); + displayId = ADISPLAY_ID_NONE; + } + + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); + + // Moving an external trackball or mouse should wake the device. + // We don't do this for internal cursor devices to prevent them from waking up + // the device in your pocket. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + // Synthesize key down from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + // Send motion event. + if (downChanged || moved || scrolled || buttonsChanged) { + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = lastButtonState; + int32_t motionEventAction; + if (downChanged) { + motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } else { + motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; + } + + if (buttonsReleased) { + BitSet32 released(buttonsReleased); + while (!released.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); + buttonState &= ~actionButton; + NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&releaseArgs); + } + } + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + + if (buttonsPressed) { + BitSet32 pressed(buttonsPressed); + while (!pressed.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); + buttonState |= actionButton; + NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&pressArgs); + } + } + + ALOG_ASSERT(buttonState == currentButtonState); + + // Send hover move after UP to tell the application that the mouse is hovering now. + if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&hoverArgs); + } + + // Send scroll events. + if (scrolled) { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + } + + // Synthesize key up from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + mCursorMotionAccumulator.finishSync(); + mCursorScrollAccumulator.finishSync(); +} + +int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + +void CursorInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +std::optional CursorInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mParameters.mode == Parameters::MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + // If the device is orientationAware and not a mouse, + // it expects to dispatch events to any display + return std::make_optional(ADISPLAY_ID_NONE); + } + } + return std::nullopt; +} + +// --- RotaryEncoderInputMapper --- + +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) + : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { + mSource = AINPUT_SOURCE_ROTARY_ENCODER; +} + +RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} + +uint32_t RotaryEncoderInputMapper::getSources() { + return mSource; +} + +void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { + float res = 0.0f; + if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { + ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); + } + if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), + mScalingFactor)) { + ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," + "default to 1.0!\n"); + mScalingFactor = 1.0f; + } + info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + res * mScalingFactor); + } +} + +void RotaryEncoderInputMapper::dump(std::string& dump) { + dump += INDENT2 "Rotary Encoder Input Mapper:\n"; + dump += StringPrintf(INDENT3 "HaveWheel: %s\n", + toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); +} + +void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + if (!changes) { + mRotaryEncoderScrollAccumulator.configure(getDevice()); + } + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } else { + mOrientation = DISPLAY_ORIENTATION_0; + } + } +} + +void RotaryEncoderInputMapper::reset(nsecs_t when) { + mRotaryEncoderScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + mRotaryEncoderScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void RotaryEncoderInputMapper::sync(nsecs_t when) { + PointerCoords pointerCoords; + pointerCoords.clear(); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + bool scrolled = scroll != 0; + + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + // Moving the rotary encoder should wake the device (if specified). + uint32_t policyFlags = 0; + if (scrolled && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + + // Send motion event. + if (scrolled) { + int32_t metaState = mContext->getGlobalMetaState(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + + mRotaryEncoderScrollAccumulator.finishSync(); +} + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device) + : InputMapper(device), + mSource(0), + mDeviceMode(DEVICE_MODE_DISABLED), + mSurfaceWidth(-1), + mSurfaceHeight(-1), + mSurfaceLeft(0), + mSurfaceTop(0), + mPhysicalWidth(-1), + mPhysicalHeight(-1), + mPhysicalLeft(0), + mPhysicalTop(0), + mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} + +TouchInputMapper::~TouchInputMapper() {} + +uint32_t TouchInputMapper::getSources() { + return mSource; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mDeviceMode != DEVICE_MODE_DISABLED) { + info->addMotionRange(mOrientedRanges.x); + info->addMotionRange(mOrientedRanges.y); + info->addMotionRange(mOrientedRanges.pressure); + + if (mOrientedRanges.haveSize) { + info->addMotionRange(mOrientedRanges.size); + } + + if (mOrientedRanges.haveTouchSize) { + info->addMotionRange(mOrientedRanges.touchMajor); + info->addMotionRange(mOrientedRanges.touchMinor); + } + + if (mOrientedRanges.haveToolSize) { + info->addMotionRange(mOrientedRanges.toolMajor); + info->addMotionRange(mOrientedRanges.toolMinor); + } + + if (mOrientedRanges.haveOrientation) { + info->addMotionRange(mOrientedRanges.orientation); + } + + if (mOrientedRanges.haveDistance) { + info->addMotionRange(mOrientedRanges.distance); + } + + if (mOrientedRanges.haveTilt) { + info->addMotionRange(mOrientedRanges.tilt); + } + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + } + info->setButtonUnderPad(mParameters.hasButtonUnderPad); + } +} + +void TouchInputMapper::dump(std::string& dump) { + dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); + dumpParameters(dump); + dumpVirtualKeys(dump); + dumpRawPointerAxes(dump); + dumpCalibration(dump); + dumpAffineTransformation(dump); + dumpSurface(dump); + + dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); + dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); + dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); + dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); + dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); + dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); + dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); + dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); + dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); + dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); + dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); + dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); + dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); + + dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); + dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", + mLastRawState.rawPointerData.pointerCount); + for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { + const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " + "toolType=%d, isHovering=%s\n", + i, pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, + pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, + pointer.distance, pointer.toolType, toString(pointer.isHovering)); + } + + dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", + mLastCookedState.buttonState); + dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", + mLastCookedState.cookedPointerData.pointerCount); + for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { + const PointerProperties& pointerProperties = + mLastCookedState.cookedPointerData.pointerProperties[i]; + const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " + "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " + "toolMinor=%0.3f, " + "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " + "toolType=%d, isHovering=%s\n", + i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), + pointerProperties.toolType, + toString(mLastCookedState.cookedPointerData.isHovering(i))); + } + + dump += INDENT3 "Stylus Fusion:\n"; + dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", + toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", + mExternalStylusFusionTimeout); + dump += INDENT3 "External Stylus State:\n"; + dumpStylusState(dump, mExternalStylusState); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); + dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); + dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); + dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); + dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); + dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); + } +} + +const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { + switch (deviceMode) { + case DEVICE_MODE_DISABLED: + return "disabled"; + case DEVICE_MODE_DIRECT: + return "direct"; + case DEVICE_MODE_UNSCALED: + return "unscaled"; + case DEVICE_MODE_NAVIGATION: + return "navigation"; + case DEVICE_MODE_POINTER: + return "pointer"; + } + return "unknown"; +} + +void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + mConfig = *config; + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + + // Configure common accumulators. + mCursorScrollAccumulator.configure(getDevice()); + mTouchButtonAccumulator.configure(getDevice()); + + // Configure absolute axis information. + configureRawPointerAxes(); + + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { + // Update location calibration to reflect current settings + updateAffineTransformation(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + // Update pointer speed. + mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + } + + bool resetNeeded = false; + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES | + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + // Configure device sources, surface dimensions, orientation and + // scaling factors. + configureSurface(when, &resetNeeded); + } + + if (changes && resetNeeded) { + // Send reset, unless this is the first time the device has been configured, + // in which case the reader will call reset itself after all mappers are ready. + getDevice()->notifyReset(when); + } +} + +void TouchInputMapper::resolveExternalStylusPresence() { + std::vector devices; + mContext->getExternalStylusDevices(devices); + mExternalStylusConnected = !devices.empty(); + + if (!mExternalStylusConnected) { + resetExternalStylus(); + } +} + +void TouchInputMapper::configureParameters() { + // Use the pointer presentation mode for devices that do not support distinct + // multitouch. The spot-based presentation relies on being able to accurately + // locate two or more fingers on the touch pad. + mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) + ? Parameters::GESTURE_MODE_SINGLE_TOUCH + : Parameters::GESTURE_MODE_MULTI_TOUCH; + + String8 gestureModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), + gestureModeString)) { + if (gestureModeString == "single-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; + } else if (gestureModeString == "multi-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; + } else if (gestureModeString != "default") { + ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); + } + } + + if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + // The device is a touch screen. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + // The device is a pointing device like a track pad. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || + getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is a touch pad of unknown purpose. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + + mParameters.hasButtonUnderPad = + getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); + + String8 deviceTypeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { + if (deviceTypeString == "touchScreen") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (deviceTypeString == "touchPad") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "touchNavigation") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (deviceTypeString != "default") { + ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + } + } + + mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + mParameters.associatedDisplayIsExternal = false; + if (mParameters.orientationAware || + mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || + mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.hasAssociatedDisplay = true; + if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { + mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + String8 uniqueDisplayId; + getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), + uniqueDisplayId); + mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); + } + } + if (getDevice()->getAssociatedDisplayPort()) { + mParameters.hasAssociatedDisplay = true; + } + + // Initial downs on external touch devices should wake the device. + // Normally we don't do this for internal touch screens to prevent them from waking + // up in your pocket but you can enable it using the input device configuration. + mParameters.wake = getDevice()->isExternal(); + getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); +} + +void TouchInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + + switch (mParameters.gestureMode) { + case Parameters::GESTURE_MODE_SINGLE_TOUCH: + dump += INDENT4 "GestureMode: single-touch\n"; + break; + case Parameters::GESTURE_MODE_MULTI_TOUCH: + dump += INDENT4 "GestureMode: multi-touch\n"; + break; + default: + assert(false); + } + + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + dump += INDENT4 "DeviceType: touchScreen\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + dump += INDENT4 "DeviceType: touchPad\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: + dump += INDENT4 "DeviceType: touchNavigation\n"; + break; + case Parameters::DEVICE_TYPE_POINTER: + dump += INDENT4 "DeviceType: pointer\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " + "displayId='%s'\n", + toString(mParameters.hasAssociatedDisplay), + toString(mParameters.associatedDisplayIsExternal), + mParameters.uniqueDisplayId.c_str()); + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void TouchInputMapper::configureRawPointerAxes() { + mRawPointerAxes.clear(); +} + +void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { + dump += INDENT3 "Raw Touch Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); +} + +bool TouchInputMapper::hasExternalStylus() const { + return mExternalStylusConnected; +} + +/** + * Determine which DisplayViewport to use. + * 1. If display port is specified, return the matching viewport. If matching viewport not + * found, then return. + * 2. If a device has associated display, get the matching viewport by either unique id or by + * the display type (internal or external). + * 3. Otherwise, use a non-display viewport. + */ +std::optional TouchInputMapper::findViewport() { + if (mParameters.hasAssociatedDisplay) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // Check if uniqueDisplayId is specified in idc file. + if (!mParameters.uniqueDisplayId.empty()) { + return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); + } + + ViewportType viewportTypeToUse; + if (mParameters.associatedDisplayIsExternal) { + viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; + } else { + viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; + } + + std::optional viewport = + mConfig.getDisplayViewportByType(viewportTypeToUse); + if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { + ALOGW("Input device %s should be associated with external display, " + "fallback to internal one for the external viewport is not found.", + getDeviceName().c_str()); + viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return viewport; + } + + // No associated display, return a non-display viewport. + DisplayViewport newViewport; + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + newViewport.setNonDisplayViewport(rawWidth, rawHeight); + return std::make_optional(newViewport); +} + +void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { + int32_t oldDeviceMode = mDeviceMode; + + resolveExternalStylusPresence(); + + // Determine device mode. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && + mConfig.pointerGesturesEnabled) { + mSource = AINPUT_SOURCE_MOUSE; + mDeviceMode = DEVICE_MODE_POINTER; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && + mParameters.hasAssociatedDisplay) { + mSource = AINPUT_SOURCE_TOUCHSCREEN; + mDeviceMode = DEVICE_MODE_DIRECT; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + if (hasExternalStylus()) { + mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mDeviceMode = DEVICE_MODE_NAVIGATION; + } else { + mSource = AINPUT_SOURCE_TOUCHPAD; + mDeviceMode = DEVICE_MODE_UNSCALED; + } + + // Ensure we have valid X and Y axes. + if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { + ALOGW("Touch device '%s' did not report support for X or Y axis! " + "The device will be inoperable.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Get associated display dimensions. + std::optional newViewport = findViewport(); + if (!newViewport) { + ALOGI("Touch device '%s' could not query the properties of its associated " + "display. The device will be inoperable until the display size " + "becomes available.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + + bool viewportChanged = mViewport != *newViewport; + if (viewportChanged) { + mViewport = *newViewport; + + if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { + // Convert rotated viewport to natural surface coordinates. + int32_t naturalLogicalWidth, naturalLogicalHeight; + int32_t naturalPhysicalWidth, naturalPhysicalHeight; + int32_t naturalPhysicalLeft, naturalPhysicalTop; + int32_t naturalDeviceWidth, naturalDeviceHeight; + switch (mViewport.orientation) { + case DISPLAY_ORIENTATION_90: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalTop = mViewport.physicalLeft; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_180: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + case DISPLAY_ORIENTATION_270: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.physicalTop; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_0: + default: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.physicalLeft; + naturalPhysicalTop = mViewport.physicalTop; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + } + + if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { + ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); + naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; + naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; + } + + mPhysicalWidth = naturalPhysicalWidth; + mPhysicalHeight = naturalPhysicalHeight; + mPhysicalLeft = naturalPhysicalLeft; + mPhysicalTop = naturalPhysicalTop; + + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + + mSurfaceOrientation = + mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; + } else { + mPhysicalWidth = rawWidth; + mPhysicalHeight = rawHeight; + mPhysicalLeft = 0; + mPhysicalTop = 0; + + mSurfaceWidth = rawWidth; + mSurfaceHeight = rawHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceOrientation = DISPLAY_ORIENTATION_0; + } + } + + // If moving between pointer modes, need to reset some state. + bool deviceModeChanged = mDeviceMode != oldDeviceMode; + if (deviceModeChanged) { + mOrientedRanges.clear(); + } + + // Create or update pointer controller if needed. + if (mDeviceMode == DEVICE_MODE_POINTER || + (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { + if (mPointerController == nullptr || viewportChanged) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + } else { + mPointerController.clear(); + } + + if (viewportChanged || deviceModeChanged) { + ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + "display id %d", + getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, + mSurfaceOrientation, mDeviceMode, mViewport.displayId); + + // Configure X and Y factors. + mXScale = float(mSurfaceWidth) / rawWidth; + mYScale = float(mSurfaceHeight) / rawHeight; + mXTranslate = -mSurfaceLeft; + mYTranslate = -mSurfaceTop; + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; + mOrientedRanges.x.source = mSource; + mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; + mOrientedRanges.y.source = mSource; + + configureVirtualKeys(); + + // Scale factor for terms that are not oriented in a particular axis. + // If the pixels are square then xScale == yScale otherwise we fake it + // by choosing an average. + mGeometricScale = avg(mXScale, mYScale); + + // Size of diagonal axis. + float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Size factors. + if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; + } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; + } else { + mSizeScale = 0.0f; + } + + mOrientedRanges.haveTouchSize = true; + mOrientedRanges.haveToolSize = true; + mOrientedRanges.haveSize = true; + + mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; + mOrientedRanges.touchMajor.source = mSource; + mOrientedRanges.touchMajor.min = 0; + mOrientedRanges.touchMajor.max = diagonalSize; + mOrientedRanges.touchMajor.flat = 0; + mOrientedRanges.touchMajor.fuzz = 0; + mOrientedRanges.touchMajor.resolution = 0; + + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; + + mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; + mOrientedRanges.toolMajor.source = mSource; + mOrientedRanges.toolMajor.min = 0; + mOrientedRanges.toolMajor.max = diagonalSize; + mOrientedRanges.toolMajor.flat = 0; + mOrientedRanges.toolMajor.fuzz = 0; + mOrientedRanges.toolMajor.resolution = 0; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; + mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; + + mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; + mOrientedRanges.size.source = mSource; + mOrientedRanges.size.min = 0; + mOrientedRanges.size.max = 1.0; + mOrientedRanges.size.flat = 0; + mOrientedRanges.size.fuzz = 0; + mOrientedRanges.size.resolution = 0; + } else { + mSizeScale = 0.0f; + } + + // Pressure factors. + mPressureScale = 0; + float pressureMax = 1.0; + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || + mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { + if (mCalibration.havePressureScale) { + mPressureScale = mCalibration.pressureScale; + pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; + } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { + mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; + } + } + + mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; + mOrientedRanges.pressure.source = mSource; + mOrientedRanges.pressure.min = 0; + mOrientedRanges.pressure.max = pressureMax; + mOrientedRanges.pressure.flat = 0; + mOrientedRanges.pressure.fuzz = 0; + mOrientedRanges.pressure.resolution = 0; + + // Tilt + mTiltXCenter = 0; + mTiltXScale = 0; + mTiltYCenter = 0; + mTiltYScale = 0; + mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; + if (mHaveTilt) { + mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); + mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); + mTiltXScale = M_PI / 180; + mTiltYScale = M_PI / 180; + + mOrientedRanges.haveTilt = true; + + mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; + mOrientedRanges.tilt.source = mSource; + mOrientedRanges.tilt.min = 0; + mOrientedRanges.tilt.max = M_PI_2; + mOrientedRanges.tilt.flat = 0; + mOrientedRanges.tilt.fuzz = 0; + mOrientedRanges.tilt.resolution = 0; + } + + // Orientation + mOrientationScale = 0; + if (mHaveTilt) { + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI; + mOrientedRanges.orientation.max = M_PI; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } else if (mCalibration.orientationCalibration != + Calibration::ORIENTATION_CALIBRATION_NONE) { + if (mCalibration.orientationCalibration == + Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { + if (mRawPointerAxes.orientation.valid) { + if (mRawPointerAxes.orientation.maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; + } else if (mRawPointerAxes.orientation.minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + } else { + mOrientationScale = 0; + } + } + } + + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } + + // Distance + mDistanceScale = 0; + if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { + if (mCalibration.haveDistanceScale) { + mDistanceScale = mCalibration.distanceScale; + } else { + mDistanceScale = 1.0f; + } + } + + mOrientedRanges.haveDistance = true; + + mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; + mOrientedRanges.distance.source = mSource; + mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; + mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; + mOrientedRanges.distance.flat = 0; + mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; + mOrientedRanges.distance.resolution = 0; + } + + // Compute oriented precision, scales and ranges. + // Note that the maximum value reported is an inclusive maximum value so it is one + // unit less than the total width or height of surface. + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + + mOrientedRanges.x.min = mYTranslate; + mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; + + mOrientedRanges.y.min = mXTranslate; + mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; + break; + + default: + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + + mOrientedRanges.x.min = mXTranslate; + mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; + + mOrientedRanges.y.min = mYTranslate; + mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; + break; + } + + // Location + updateAffineTransformation(); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + // Compute pointer gesture detection parameters. + float rawDiagonal = hypotf(rawWidth, rawHeight); + float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Scale movements such that one whole swipe of the touch pad covers a + // given area relative to the diagonal size of the display when no acceleration + // is applied. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + mPointerXMovementScale = + mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYMovementScale = mPointerXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mPointerXZoomScale = + mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYZoomScale = mPointerXZoomScale; + + // Max width between pointers to detect a swipe gesture is more than some fraction + // of the diagonal axis of the touch pad. Touches that are wider than this are + // translated into freeform gestures. + mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; + + // Abort current pointer usages because the state has changed. + abortPointerUsage(when, 0 /*policyFlags*/); + } + + // Inform the dispatcher about the changes. + *outResetNeeded = true; + bumpGeneration(); + } +} + +void TouchInputMapper::dumpSurface(std::string& dump) { + dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); + dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); + dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); + dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); + dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); + dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); + dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); +} + +void TouchInputMapper::configureVirtualKeys() { + std::vector virtualKeyDefinitions; + getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + int32_t touchScreenLeft = mRawPointerAxes.x.minValue; + int32_t touchScreenTop = mRawPointerAxes.y.minValue; + int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); + int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); + + for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { + VirtualKey virtualKey; + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + int32_t dummyKeyMetaState; + uint32_t flags; + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, + &dummyKeyMetaState, &flags)) { + ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + continue; // drop the key + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitRight = + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitTop = + (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + virtualKey.hitBottom = + (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + mVirtualKeys.push_back(virtualKey); + } +} + +void TouchInputMapper::dumpVirtualKeys(std::string& dump) { + if (!mVirtualKeys.empty()) { + dump += INDENT3 "Virtual Keys:\n"; + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " + "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", + i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, + virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } +} + +void TouchInputMapper::parseCalibration() { + const PropertyMap& in = getDevice()->getConfiguration(); + Calibration& out = mCalibration; + + // Size + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; + String8 sizeCalibrationString; + if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { + if (sizeCalibrationString == "none") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } else if (sizeCalibrationString == "geometric") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } else if (sizeCalibrationString == "diameter") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; + } else if (sizeCalibrationString == "box") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; + } else if (sizeCalibrationString == "area") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; + } else if (sizeCalibrationString != "default") { + ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); + } + } + + out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); + out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); + out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); + + // Pressure + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; + String8 pressureCalibrationString; + if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { + if (pressureCalibrationString == "none") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } else if (pressureCalibrationString == "physical") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } else if (pressureCalibrationString == "amplitude") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else if (pressureCalibrationString != "default") { + ALOGW("Invalid value for touch.pressure.calibration: '%s'", + pressureCalibrationString.string()); + } + } + + out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); + + // Orientation + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; + String8 orientationCalibrationString; + if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { + if (orientationCalibrationString == "none") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } else if (orientationCalibrationString == "interpolated") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else if (orientationCalibrationString == "vector") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; + } else if (orientationCalibrationString != "default") { + ALOGW("Invalid value for touch.orientation.calibration: '%s'", + orientationCalibrationString.string()); + } + } + + // Distance + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; + String8 distanceCalibrationString; + if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + if (distanceCalibrationString == "none") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } else if (distanceCalibrationString == "scaled") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else if (distanceCalibrationString != "default") { + ALOGW("Invalid value for touch.distance.calibration: '%s'", + distanceCalibrationString.string()); + } + } + + out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; + String8 coverageCalibrationString; + if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + if (coverageCalibrationString == "none") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } else if (coverageCalibrationString == "box") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; + } else if (coverageCalibrationString != "default") { + ALOGW("Invalid value for touch.coverage.calibration: '%s'", + coverageCalibrationString.string()); + } + } +} + +void TouchInputMapper::resolveCalibration() { + // Size + if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } + } else { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } + + // Pressure + if (mRawPointerAxes.pressure.valid) { + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } + } else { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } + + // Orientation + if (mRawPointerAxes.orientation.valid) { + if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } + } else { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } + + // Distance + if (mRawPointerAxes.distance.valid) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } + } else { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } + + // Coverage + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { + mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } +} + +void TouchInputMapper::dumpCalibration(std::string& dump) { + dump += INDENT3 "Calibration:\n"; + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NONE: + dump += INDENT4 "touch.size.calibration: none\n"; + break; + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + dump += INDENT4 "touch.size.calibration: geometric\n"; + break; + case Calibration::SIZE_CALIBRATION_DIAMETER: + dump += INDENT4 "touch.size.calibration: diameter\n"; + break; + case Calibration::SIZE_CALIBRATION_BOX: + dump += INDENT4 "touch.size.calibration: box\n"; + break; + case Calibration::SIZE_CALIBRATION_AREA: + dump += INDENT4 "touch.size.calibration: area\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveSizeScale) { + dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); + } + + if (mCalibration.haveSizeBias) { + dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); + } + + if (mCalibration.haveSizeIsSummed) { + dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", + toString(mCalibration.sizeIsSummed)); + } + + // Pressure + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_NONE: + dump += INDENT4 "touch.pressure.calibration: none\n"; + break; + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + dump += INDENT4 "touch.pressure.calibration: physical\n"; + break; + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + dump += INDENT4 "touch.pressure.calibration: amplitude\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.havePressureScale) { + dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_NONE: + dump += INDENT4 "touch.orientation.calibration: none\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + dump += INDENT4 "touch.orientation.calibration: interpolated\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: + dump += INDENT4 "touch.orientation.calibration: vector\n"; + break; + default: + ALOG_ASSERT(false); + } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_NONE: + dump += INDENT4 "touch.distance.calibration: none\n"; + break; + case Calibration::DISTANCE_CALIBRATION_SCALED: + dump += INDENT4 "touch.distance.calibration: scaled\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveDistanceScale) { + dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); + } + + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_NONE: + dump += INDENT4 "touch.coverage.calibration: none\n"; + break; + case Calibration::COVERAGE_CALIBRATION_BOX: + dump += INDENT4 "touch.coverage.calibration: box\n"; + break; + default: + ALOG_ASSERT(false); + } +} + +void TouchInputMapper::dumpAffineTransformation(std::string& dump) { + dump += INDENT3 "Affine Transformation:\n"; + + dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); + dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); + dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); + dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); + dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); + dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); +} + +void TouchInputMapper::updateAffineTransformation() { + mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), + mSurfaceOrientation); +} + +void TouchInputMapper::reset(nsecs_t when) { + mCursorButtonAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + mTouchButtonAccumulator.reset(getDevice()); + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mRawStatesPending.clear(); + mCurrentRawState.clear(); + mCurrentCookedState.clear(); + mLastRawState.clear(); + mLastCookedState.clear(); + mPointerUsage = POINTER_USAGE_NONE; + mSentHoverEnter = false; + mHavePointerIds = false; + mCurrentMotionAborted = false; + mDownTime = 0; + + mCurrentVirtualKey.down = false; + + mPointerGesture.reset(); + mPointerSimple.reset(); + resetExternalStylus(); + + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } + + InputMapper::reset(when); +} + +void TouchInputMapper::resetExternalStylus() { + mExternalStylusState.clear(); + mExternalStylusId = -1; + mExternalStylusFusionTimeout = LLONG_MAX; + mExternalStylusDataPending = false; +} + +void TouchInputMapper::clearStylusDataPendingFlags() { + mExternalStylusDataPending = false; + mExternalStylusFusionTimeout = LLONG_MAX; +} + +void TouchInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void TouchInputMapper::sync(nsecs_t when) { + const RawState* last = + mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); + + // Push a new state. + mRawStatesPending.emplace_back(); + + RawState* next = &mRawStatesPending.back(); + next->clear(); + next->when = when; + + // Sync button state. + next->buttonState = + mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); + + // Sync scroll + next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); + next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); + mCursorScrollAccumulator.finishSync(); + + // Sync touch + syncTouch(when, next); + + // Assign pointer ids. + if (!mHavePointerIds) { + assignPointerIds(last, next); + } + +#if DEBUG_RAW_EVENTS + ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " + "hovering ids 0x%08x -> 0x%08x", + last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, + last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, + last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); +#endif + + processRawTouches(false /*timeout*/); +} + +void TouchInputMapper::processRawTouches(bool timeout) { + if (mDeviceMode == DEVICE_MODE_DISABLED) { + // Drop all input if the device is disabled. + mCurrentRawState.clear(); + mRawStatesPending.clear(); + return; + } + + // Drain any pending touch states. The invariant here is that the mCurrentRawState is always + // valid and must go through the full cook and dispatch cycle. This ensures that anything + // touching the current state will only observe the events that have been dispatched to the + // rest of the pipeline. + const size_t N = mRawStatesPending.size(); + size_t count; + for (count = 0; count < N; count++) { + const RawState& next = mRawStatesPending[count]; + + // A failure to assign the stylus id means that we're waiting on stylus data + // and so should defer the rest of the pipeline. + if (assignExternalStylusId(next, timeout)) { + break; + } + + // All ready to go. + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(next); + if (mCurrentRawState.when < mLastRawState.when) { + mCurrentRawState.when = mLastRawState.when; + } + cookAndDispatch(mCurrentRawState.when); + } + if (count != 0) { + mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); + } + + if (mExternalStylusDataPending) { + if (timeout) { + nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(mLastRawState); +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, synthesizing event with new stylus data"); +#endif + cookAndDispatch(when); + } else if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::cookAndDispatch(nsecs_t when) { + // Always start with a clean state. + mCurrentCookedState.clear(); + + // Apply stylus buttons to current raw state. + applyExternalStylusButtonState(when); + + // Handle policy on initial down or hover events. + bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + mCurrentRawState.rawPointerData.pointerCount != 0; + + uint32_t policyFlags = 0; + bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; + if (initialDown || buttonsPressed) { + // If this is a touch screen, hide the pointer on an initial down. + if (mDeviceMode == DEVICE_MODE_DIRECT) { + getContext()->fadePointer(); + } + + if (mParameters.wake) { + policyFlags |= POLICY_FLAG_WAKE; + } + } + + // Consume raw off-screen touches before cooking pointer data. + // If touches are consumed, subsequent code will not receive any pointer data. + if (consumeRawTouches(when, policyFlags)) { + mCurrentRawState.rawPointerData.clear(); + } + + // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure + // with cooked pointer data that has the same ids and indices as the raw data. + // The following code can use either the raw or cooked data, as needed. + cookPointerData(); + + // Apply stylus pressure to current cooked state. + applyExternalStylusTouchState(when); + + // Synthesize key down from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Dispatch the touches either directly or by translation through a pointer on screen. + if (mDeviceMode == DEVICE_MODE_POINTER) { + for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mCurrentCookedState.fingerIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { + mCurrentCookedState.mouseIdBits.markBit(id); + } + } + for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } + } + + // Stylus takes precedence over all tools, then mouse, then finger. + PointerUsage pointerUsage = mPointerUsage; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + mCurrentCookedState.mouseIdBits.clear(); + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_STYLUS; + } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_MOUSE; + } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || + isPointerDown(mCurrentRawState.buttonState)) { + pointerUsage = POINTER_USAGE_GESTURES; + } + + dispatchPointerUsage(when, policyFlags, pointerUsage); + } else { + if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && + mPointerController != nullptr) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); + } + + if (!mCurrentMotionAborted) { + dispatchButtonRelease(when, policyFlags); + dispatchHoverExit(when, policyFlags); + dispatchTouches(when, policyFlags); + dispatchHoverEnterAndMove(when, policyFlags); + dispatchButtonPress(when, policyFlags); + } + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentMotionAborted = false; + } + } + + // Synthesize key up from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Clear some transient state. + mCurrentRawState.rawVScroll = 0; + mCurrentRawState.rawHScroll = 0; + + // Copy current touch to last touch in preparation for the next cycle. + mLastRawState.copyFrom(mCurrentRawState); + mLastCookedState.copyFrom(mCurrentCookedState); +} + +void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { + mCurrentRawState.buttonState |= mExternalStylusState.buttons; + } +} + +void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { + CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; + const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + + if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { + float pressure = mExternalStylusState.pressure; + if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { + const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); + pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + + PointerProperties& properties = + currentPointerData.editPointerPropertiesWithId(mExternalStylusId); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + properties.toolType = mExternalStylusState.toolType; + } + } +} + +bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { + if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { + return false; + } + + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + state.rawPointerData.pointerCount != 0; + if (initialDown) { + if (mExternalStylusState.pressure != 0.0f) { +#if DEBUG_STYLUS_FUSION + ALOGD("Have both stylus and touch data, beginning fusion"); +#endif + mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + } else if (timeout) { +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, assuming touch is not a stylus."); +#endif + resetExternalStylus(); + } else { + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } +#if DEBUG_STYLUS_FUSION + ALOGD("No stylus data but stylus is connected, requesting timeout " + "(%" PRId64 "ms)", + mExternalStylusFusionTimeout); +#endif + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; + } + } + + // Check if the stylus pointer has gone up. + if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { +#if DEBUG_STYLUS_FUSION + ALOGD("Stylus pointer is going up"); +#endif + mExternalStylusId = -1; + } + + return false; +} + +void TouchInputMapper::timeoutExpired(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + if (mPointerUsage == POINTER_USAGE_GESTURES) { + dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); + } + } else if (mDeviceMode == DEVICE_MODE_DIRECT) { + if (mExternalStylusFusionTimeout < when) { + processRawTouches(true /*timeout*/); + } else if (mExternalStylusFusionTimeout != LLONG_MAX) { + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::updateExternalStylusState(const StylusState& state) { + mExternalStylusState.copyFrom(state); + if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { + // We're either in the middle of a fused stream of data or we're waiting on data before + // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus + // data. + mExternalStylusDataPending = true; + processRawTouches(false /*timeout*/); + } +} + +bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { + // Check for release of a virtual key. + if (mCurrentVirtualKey.down) { + if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + return true; + } + + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return true; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation but do not consume the touch yet. + // This is useful when the user swipes through from the virtual key area + // into the main display surface. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, + mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | + AKEY_EVENT_FLAG_CANCELED); + } + } + + if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer just went down. Check for virtual key press or off-screen touches. + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); + if (!isPointInsideSurface(pointer.x, pointer.y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; + mCurrentVirtualKey.ignored = + mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, + virtualKey->scanCode); + + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + } + } + return true; + } + } + + // Disable all virtual key touches that happen within a short time interval of the + // most recent touch within the screen area. The idea is to filter out stray + // virtual key presses when interacting with the touch screen. + // + // Problems we're trying to solve: + // + // 1. While scrolling a list or dragging the window shade, the user swipes down into a + // virtual key area that is implemented by a separate touch panel and accidentally + // triggers a virtual key. + // + // 2. While typing in the on screen keyboard, the user taps slightly outside the screen + // area and accidentally triggers a virtual key. This often happens when virtual keys + // are layed out below the screen near to where the on screen keyboard's space bar + // is displayed. + if (mConfig.virtualKeyQuietTime > 0 && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); + } + return false; +} + +void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags) { + int32_t keyCode = mCurrentVirtualKey.keyCode; + int32_t scanCode = mCurrentVirtualKey.scanCode; + nsecs_t downTime = mCurrentVirtualKey.downTime; + int32_t metaState = mContext->getGlobalMetaState(); + policyFlags |= POLICY_FLAG_VIRTUAL; + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); + getListener()->notifyKey(&args); +} + +void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + if (!currentIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mCurrentMotionAborted = true; + } +} + +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + if (currentIdBits == lastIdBits) { + if (!currentIdBits.isEmpty()) { + // No pointer id changes so this is a move event. + // The listener takes care of batching moves so we don't have to deal with that here. + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } else { + // 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 moveIdBits(lastIdBits.value & currentIdBits.value); + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = + updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, moveIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + + // Dispatch pointer up events. + while (!upIdBits.isEmpty()) { + uint32_t upId = upIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); + } + + // 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 && !moveIdBits.isEmpty()) { + ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + + // Dispatch pointer down events using the new pointer locations. + while (!downIdBits.isEmpty()) { + uint32_t downId = downIdBits.clearFirstMarkedBit(); + dispatchedIdBits.markBit(downId); + + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. + mDownTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, + downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } +} + +void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { + if (mSentHoverEnter && + (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || + !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { + int32_t metaState = getContext()->getGlobalMetaState(); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastCookedState.buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, + mOrientedYPrecision, mDownTime); + mSentHoverEnter = false; + } +} + +void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { + if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && + !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + if (!mSentHoverEnter) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mSentHoverEnter = true; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { + BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!releasedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); + buttonState &= ~actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + actionButton, 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { + BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!pressedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); + buttonState |= actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, + 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { + if (!cookedPointerData.touchingIdBits.isEmpty()) { + return cookedPointerData.touchingIdBits; + } + return cookedPointerData.hoveringIdBits; +} + +void TouchInputMapper::cookPointerData() { + uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; + + mCurrentCookedState.cookedPointerData.clear(); + mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; + mCurrentCookedState.cookedPointerData.hoveringIdBits = + mCurrentRawState.rawPointerData.hoveringIdBits; + mCurrentCookedState.cookedPointerData.touchingIdBits = + mCurrentRawState.rawPointerData.touchingIdBits; + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentCookedState.buttonState = 0; + } else { + mCurrentCookedState.buttonState = mCurrentRawState.buttonState; + } + + // Walk through the the active pointers and map device coordinates onto + // surface coordinates and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; + + // Size + float touchMajor, touchMinor, toolMajor, toolMinor, size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + case Calibration::SIZE_CALIBRATION_DIAMETER: + case Calibration::SIZE_CALIBRATION_BOX: + case Calibration::SIZE_CALIBRATION_AREA: + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { + touchMajor = in.touchMajor; + touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + toolMajor = in.toolMajor; + toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.touchMajor.valid) { + toolMajor = touchMajor = in.touchMajor; + toolMinor = touchMinor = + mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.toolMajor.valid) { + touchMajor = toolMajor = in.toolMajor; + touchMinor = toolMinor = + mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + } else { + ALOG_ASSERT(false, + "No touch or tool axes. " + "Size calibration should have been resolved to NONE."); + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + } + + if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { + uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); + if (touchingCount > 1) { + touchMajor /= touchingCount; + touchMinor /= touchingCount; + toolMajor /= touchingCount; + toolMinor /= touchingCount; + size /= touchingCount; + } + } + + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { + touchMajor *= mGeometricScale; + touchMinor *= mGeometricScale; + toolMajor *= mGeometricScale; + toolMinor *= mGeometricScale; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { + touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; + touchMinor = touchMajor; + toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; + toolMinor = toolMajor; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { + touchMinor = touchMajor; + toolMinor = toolMajor; + } + + mCalibration.applySizeScaleAndBias(&touchMajor); + mCalibration.applySizeScaleAndBias(&touchMinor); + mCalibration.applySizeScaleAndBias(&toolMajor); + mCalibration.applySizeScaleAndBias(&toolMinor); + size *= mSizeScale; + break; + default: + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + break; + } + + // Pressure + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = in.pressure * mPressureScale; + break; + default: + pressure = in.isHovering ? 0 : 1; + break; + } + + // Tilt and Orientation + float tilt; + float orientation; + if (mHaveTilt) { + float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; + float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; + orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); + tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); + } else { + tilt = 0; + + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mOrientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float confidence = hypotf(c1, c2); + float scale = 1.0f + confidence / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; + } + break; + } + default: + orientation = 0; + } + } + + // Distance + float distance; + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_SCALED: + distance = in.distance * mDistanceScale; + break; + default: + distance = 0; + } + + // Coverage + int32_t rawLeft, rawTop, rawRight, rawBottom; + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_BOX: + rawLeft = (in.toolMinor & 0xffff0000) >> 16; + rawRight = in.toolMinor & 0x0000ffff; + rawBottom = in.toolMajor & 0x0000ffff; + rawTop = (in.toolMajor & 0xffff0000) >> 16; + break; + default: + rawLeft = rawTop = rawRight = rawBottom = 0; + break; + } + + // Adjust X,Y coords for device calibration + // TODO: Adjust coverage coords? + float xTransformed = in.x, yTransformed = in.y; + mAffineTransform.applyTo(xTransformed, yTransformed); + + // Adjust X, Y, and coverage coords for surface orientation. + float x, y; + float left, top, right, bottom; + + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + orientation -= M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; + y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + orientation -= M_PI; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; + y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + orientation += M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation > mOrientedRanges.orientation.max) { + orientation -= + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + default: + x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + break; + } + + // Write output coords. + PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); + out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); + } else { + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + } + + // Write output properties. + PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; + uint32_t id = in.id; + properties.clear(); + properties.id = id; + properties.toolType = in.toolType; + + // Write id index. + mCurrentCookedState.cookedPointerData.idToIndex[id] = i; + } +} + +void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, + PointerUsage pointerUsage) { + if (pointerUsage != mPointerUsage) { + abortPointerUsage(when, policyFlags); + mPointerUsage = pointerUsage; + } + + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); + break; + case POINTER_USAGE_STYLUS: + dispatchPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + dispatchPointerMouse(when, policyFlags); + break; + default: + break; + } +} + +void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + abortPointerGestures(when, policyFlags); + break; + case POINTER_USAGE_STYLUS: + abortPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + abortPointerMouse(when, policyFlags); + break; + default: + break; + } + + mPointerUsage = POINTER_USAGE_NONE; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + bool sendEvents = + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); + if (!sendEvents) { + return; + } + if (finishPreviousGesture) { + cancelPreviousGesture = false; + } + + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + + if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); + } + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } + + // Show or hide the pointer if needed. + switch (mPointerGesture.currentGestureMode) { + case PointerGesture::NEUTRAL: + case PointerGesture::QUIET: + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { + // Remind the user of where the pointer is after finishing a gesture with spots. + mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + } + break; + case PointerGesture::TAP: + case PointerGesture::TAP_DRAG: + case PointerGesture::BUTTON_CLICK_OR_DRAG: + case PointerGesture::HOVER: + case PointerGesture::PRESS: + case PointerGesture::SWIPE: + // Unfade the pointer when the current gesture manipulates the + // area directly under the pointer. + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + break; + case PointerGesture::FREEFORM: + // Fade the pointer when the current gesture manipulates a different + // area and there are spots to guide the user experience. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } else { + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + break; + } + + // Send events! + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || + mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE || + mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture && + !mPointerGesture.lastGestureIdBits.isEmpty() && + !mPointerGesture.currentGestureIdBits.isEmpty()) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & + mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = + dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; + } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); + } + } + } + + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & + ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.clearFirstMarkedBit(); + dispatchedGestureIdBits.markBit(id); + + if (dispatchedGestureIdBits.count() == 1) { + mPointerGesture.downTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + } + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + const int32_t displayId = mPointerController->getDisplayId(); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGestureIdBits.clear(); + } else { + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureProperties[index].copyFrom( + mPointerGesture.currentGestureProperties[index]); + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Cancel previously dispatches pointers. + if (!mPointerGesture.lastGestureIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentRawState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Reset the current pointer gesture. + mPointerGesture.reset(); + mPointerVelocityControl.reset(); + + // Remove any current spots. + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } +} + +bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + // Handle TAP timeout. + if (isTimeout) { +#if DEBUG_GESTURES + ALOGD("Gestures: Processing timeout"); +#endif + + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + // The tap/drag timeout has not yet expired. + getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + + mConfig.pointerGestureTapDragInterval); + } else { + // The tap is finished. +#if DEBUG_GESTURES + ALOGD("Gestures: TAP finished"); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + return true; + } + } + + // We did not handle this timeout. + return false; + } + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + positions[count].x = pointer.x * mPointerXMovementScale; + positions[count].y = pointer.y * mPointerYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, + positions); + } + + // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning + // to NEUTRAL, then we should not generate tap event. + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && + mPointerGesture.lastGestureMode != PointerGesture::TAP && + mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { + mPointerGesture.resetTap(); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We keep the same active touch id for as long as possible. + int32_t lastActiveTouchId = mPointerGesture.activeTouchId; + int32_t activeTouchId = lastActiveTouchId; + if (activeTouchId < 0) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + mPointerGesture.firstTouchTime = when; + } + } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; + } + } + + // Determine whether we are in quiet time. + bool isQuietTime = false; + if (activeTouchId < 0) { + mPointerGesture.resetQuietTime(); + } else { + isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || + mPointerGesture.lastGestureMode == PointerGesture::SWIPE || + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && + currentFingerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && + currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + ALOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + } else if (isPointerDown(mCurrentRawState.buttonState)) { + // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " + "currentFingerCount=%d", + activeTouchId, currentFingerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0 && currentFingerCount > 1) { + int32_t bestId = -1; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = hypotf(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", + bestId, bestSpeed); +#endif + } + } + + float deltaX = 0, deltaY = 0; + if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the click will occur at the position of the anchor + // spot and all other spots will move there. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (currentFingerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } + + // Watch for taps coming out of HOVER or TAP_DRAG mode. + // Checking for taps after TAP_DRAG allows us to detect double-taps. + bool tapped = false; + if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || + mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && + lastFingerCount == 1) { + if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP"); +#endif + + mPointerGesture.tapUpTime = when; + getContext()->requestTimeoutAtTime(when + + mConfig.pointerGestureTapDragInterval); + + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = + mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.tapX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.tapY); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + tapped = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, + y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + if (mPointerGesture.tapDownTime != LLONG_MIN) { + ALOGD("Gestures: Not a TAP, %0.3fms since down", + (when - mPointerGesture.tapDownTime) * 0.000001f); + } else { + ALOGD("Gestures: Not a TAP, incompatible mode transitions"); + } +#endif + } + } + + mPointerVelocityControl.reset(); + + if (!tapped) { +#if DEBUG_GESTURES + ALOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (currentFingerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) + // The pointer follows the active touch point. + // When in HOVER, emit HOVER_MOVE events at the pointer location. + // When in TAP_DRAG, emit MOVE events at the pointer location. + ALOG_ASSERT(activeTouchId >= 0); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", + (when - mPointerGesture.tapUpTime) * 0.000001f); +#endif + } + } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } + + float deltaX = 0, deltaY = 0; + if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the hover or drag will occur at the position of the anchor spot. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + bool down; + if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP_DRAG"); +#endif + down = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: HOVER"); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } + mPointerGesture.activeGestureId = 0; + down = false; + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + down ? 1.0f : 0.0f); + + if (lastFingerCount == 0 && currentFingerCount != 0) { + mPointerGesture.resetTap(); + mPointerGesture.tapDownTime = when; + mPointerGesture.tapX = x; + mPointerGesture.tapY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) + // We need to provide feedback for each finger that goes down so we cannot wait + // for the fingers to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they + // have not moved enough to determine whether they are part of a drag or part of a + // freeform gesture, or just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a + // down at the pointer location. + // + // When the two fingers move enough or when additional fingers are added, we make + // a decision to transition into SWIPE or FREEFORM mode accordingly. + ALOG_ASSERT(activeTouchId >= 0); + + bool settled = when >= + mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; + if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && + mPointerGesture.lastGestureMode != PointerGesture::SWIPE && + mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + *outFinishPreviousGesture = true; + } else if (!settled && currentFingerCount > lastFingerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + *outCancelPreviousGesture = true; + } else { + // Continue previous gesture. + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (*outFinishPreviousGesture || *outCancelPreviousGesture) { + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; + mPointerGesture.referenceIdBits.clear(); + mPointerVelocityControl.reset(); + + // Use the centroid and pointer location as the reference points for the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + mCurrentRawState.rawPointerData + .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & + ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); + const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; + } else { + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); + } + } + + // Consider transitions from PRESS to SWIPE or MULTITOUCH. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + if (currentFingerCount > 2) { + // There are more than two pointers, switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are exactly two pointers. + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + uint32_t id1 = idBits.clearFirstMarkedBit(); + uint32_t id2 = idBits.firstMarkedBit(); + const RawPointerData::Pointer& p1 = + mCurrentRawState.rawPointerData.pointerForId(id1); + const RawPointerData::Pointer& p2 = + mCurrentRawState.rawPointerData.pointerForId(id2); + float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); + if (mutualDistance > mPointerGestureMaxSwipeWidth) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + mutualDistance, mPointerGestureMaxSwipeWidth); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && + dist2 >= mConfig.pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * + // mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dx1 = delta1.dx * mPointerXZoomScale; + float dy1 = delta1.dy * mPointerYZoomScale; + float dx2 = delta2.dx * mPointerXZoomScale; + float dy2 = delta2.dy * mPointerYZoomScale; + float dot = dx1 * dx2 + dy1 * dy2; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + } + } + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (currentFingerCount > 2) { +#if DEBUG_GESTURES + ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && + (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx = 0; + delta.dy = 0; + } + + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; + + commonDeltaX *= mPointerXMovementScale; + commonDeltaY *= mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); + mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; + } + + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // PRESS or SWIPE mode. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & + ~mCurrentCookedState.fingerIdBits.value); + !upTouchIdBits.isEmpty();) { + uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } + +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif + + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + for (uint32_t i = 0; i < currentFingerCount; i++) { + uint32_t touchId = idBits.clearFirstMarkedBit(); + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.markFirstUnmarkedBit(); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(touchId); + float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; + float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + + mPointerGesture.currentGestureProperties[i].clear(); + mPointerGesture.currentGestureProperties[i].id = gestureId; + mPointerGesture.currentGestureProperties[i].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX + deltaX); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY + deltaY); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + 1.0f); + } + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM new " + "activeGestureId=%d", + mPointerGesture.activeGestureId); +#endif + } + } + } + + mPointerController->setButtonState(mCurrentRawState.buttonState); + +#if DEBUG_GESTURES + ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif + return true; +} + +void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); + uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; + float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); + float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); + mPointerController->setPosition(x, y); + + hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); + down = !hovering; + + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[index]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; + } else { + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); +} + +void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); + uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + float deltaX = 0, deltaY = 0; + if (mLastCookedState.mouseIdBits.hasBit(id)) { + uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - + mLastRawState.rawPointerData.pointers[lastIndex].x) * + mPointerXMovementScale; + deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - + mLastRawState.rawPointerData.pointers[lastIndex].y) * + mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + down = isPointerDown(mCurrentRawState.buttonState); + hovering = !down; + + float x, y; + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + hovering ? 0.0f : 1.0f); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; + } else { + mPointerVelocityControl.reset(); + + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); + + mPointerVelocityControl.reset(); +} + +void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, + bool hovering) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t displayId = mViewport.displayId; + + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + if (mPointerSimple.down && !down) { + mPointerSimple.down = false; + + // Send up. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mPointerSimple.hovering && !hovering) { + mPointerSimple.hovering = false; + + // Send hover exit. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (down) { + if (!mPointerSimple.down) { + mPointerSimple.down = true; + mPointerSimple.downTime = when; + + // Send down. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (hovering) { + if (!mPointerSimple.hovering) { + mPointerSimple.hovering = true; + + // Send hover enter. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send hover move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { + float vscroll = mCurrentRawState.rawVScroll; + float hscroll = mCurrentRawState.rawHScroll; + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + // Send scroll. + PointerCoords pointerCoords; + pointerCoords.copyFrom(mPointerSimple.currentCoords); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Save state. + if (down || hovering) { + mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); + mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); + } else { + mPointerSimple.reset(); + } +} + +void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + dispatchPointerSimple(when, policyFlags, false, false); +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, + const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, + BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + PointerProperties pointerProperties[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = idToIndex[id]; + pointerProperties[pointerCount].copyFrom(properties[index]); + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + ALOG_ASSERT(pointerCount != 0); + + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + ALOG_ASSERT(false); + } + } + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } + const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); + const int32_t deviceId = getDeviceId(); + std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::for_each(frames.begin(), frames.end(), + [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); + getListener()->notifyMotion(&args); +} + +bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, + const PointerCoords* inCoords, + const uint32_t* inIdToIndex, + PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +void TouchInputMapper::cancelTouch(nsecs_t when) { + abortPointerUsage(when, 0 /*policyFlags*/); + abortTouches(when, 0 /* policyFlags*/); +} + +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + const float scaledX = x * mXScale; + const float scaledY = y * mYScale; + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && + scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && + scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; +} + +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { + for (const VirtualKey& virtualKey : mVirtualKeys) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return &virtualKey; + } + } + + return nullptr; +} + +void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { + uint32_t currentPointerCount = current->rawPointerData.pointerCount; + uint32_t lastPointerCount = last->rawPointerData.pointerCount; + + current->rawPointerData.clearIdBits(); + + if (currentPointerCount == 0) { + // No pointers to assign. + return; + } + + if (lastPointerCount == 0) { + // All pointers are new. + for (uint32_t i = 0; i < currentPointerCount; i++) { + uint32_t id = i; + current->rawPointerData.pointers[i].id = id; + current->rawPointerData.idToIndex[id] = i; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); + } + return; + } + + if (currentPointerCount == 1 && lastPointerCount == 1 && + current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = last->rawPointerData.pointers[0].id; + current->rawPointerData.pointers[0].id = id; + current->rawPointerData.idToIndex[id] = 0; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); + return; + } + + // General case. + // 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. + // The pointers must have the same tool type but it is possible for them to + // transition from hovering to touching or vice-versa while retaining the same id. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + const RawPointerData::Pointer& currentPointer = + current->rawPointerData.pointers[currentPointerIndex]; + const RawPointerData::Pointer& lastPointer = + last->rawPointerData.pointers[lastPointerIndex]; + if (currentPointer.toolType == lastPointer.toolType) { + int64_t deltaX = currentPointer.x - lastPointer.x; + int64_t deltaY = currentPointer.y - lastPointer.y; + + 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; + } + } + } + + // 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; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, 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. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { + while (heapSize > 0) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0;;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize && + heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, + heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + } +#endif + } + + heapSize -= 1; + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering( + currentPointerIndex)); + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 + ", distance=%" PRIu64, + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to pointers that were not matched in the process. + for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { + uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); + uint32_t id = usedIdBits.markFirstUnmarkedBit(); + + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering(currentPointerIndex)); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); +#endif + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + for (const VirtualKey& virtualKey : mVirtualKeys) { + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + + return true; +} + +std::optional TouchInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + return std::make_optional(mViewport.displayId); + } + } + return std::nullopt; +} + +// --- SingleTouchInputMapper --- + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +SingleTouchInputMapper::~SingleTouchInputMapper() {} + +void SingleTouchInputMapper::reset(nsecs_t when) { + mSingleTouchMotionAccumulator.reset(getDevice()); + + TouchInputMapper::reset(when); +} + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mSingleTouchMotionAccumulator.process(rawEvent); +} + +void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + if (mTouchButtonAccumulator.isToolActive()) { + outState->rawPointerData.pointerCount = 1; + outState->rawPointerData.idToIndex[0] = 0; + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && + mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); + outState->rawPointerData.markIdBit(0, isHovering); + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; + outPointer.id = 0; + outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); + outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); + outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + outPointer.touchMajor = 0; + outPointer.touchMinor = 0; + outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.orientation = 0; + outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); + outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); + outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + outPointer.isHovering = isHovering; + } +} + +void SingleTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); + getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); +} + +bool SingleTouchInputMapper::hasStylus() const { + return mTouchButtonAccumulator.hasStylus(); +} + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +MultiTouchInputMapper::~MultiTouchInputMapper() {} + +void MultiTouchInputMapper::reset(nsecs_t when) { + mMultiTouchMotionAccumulator.reset(getDevice()); + + mPointerIdBits.clear(); + + TouchInputMapper::reset(when); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mMultiTouchMotionAccumulator.process(rawEvent); +} + +void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); + size_t outCount = 0; + BitSet32 newPointerIdBits; + mHavePointerIds = true; + + for (size_t inIndex = 0; inIndex < inCount; inIndex++) { + const MultiTouchMotionAccumulator::Slot* inSlot = + mMultiTouchMotionAccumulator.getSlot(inIndex); + if (!inSlot->isInUse()) { + continue; + } + + if (outCount >= MAX_POINTERS) { +#if DEBUG_POINTERS + ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().c_str(), MAX_POINTERS); +#endif + break; // too many fingers! + } + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; + outPointer.x = inSlot->getX(); + outPointer.y = inSlot->getY(); + outPointer.pressure = inSlot->getPressure(); + outPointer.touchMajor = inSlot->getTouchMajor(); + outPointer.touchMinor = inSlot->getTouchMinor(); + outPointer.toolMajor = inSlot->getToolMajor(); + outPointer.toolMinor = inSlot->getToolMinor(); + outPointer.orientation = inSlot->getOrientation(); + outPointer.distance = inSlot->getDistance(); + outPointer.tiltX = 0; + outPointer.tiltY = 0; + + outPointer.toolType = inSlot->getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + } + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); + outPointer.isHovering = isHovering; + + // Assign pointer id using tracking id if available. + if (mHavePointerIds) { + int32_t trackingId = inSlot->getTrackingId(); + int32_t id = -1; + if (trackingId >= 0) { + for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { + uint32_t n = idBits.clearFirstMarkedBit(); + if (mPointerTrackingIdMap[n] == trackingId) { + id = n; + } + } + + if (id < 0 && !mPointerIdBits.isFull()) { + id = mPointerIdBits.markFirstUnmarkedBit(); + mPointerTrackingIdMap[id] = trackingId; + } + } + if (id < 0) { + mHavePointerIds = false; + outState->rawPointerData.clearIdBits(); + newPointerIdBits.clear(); + } else { + outPointer.id = id; + outState->rawPointerData.idToIndex[id] = outCount; + outState->rawPointerData.markIdBit(id, isHovering); + newPointerIdBits.markBit(id); + } + } + outCount += 1; + } + + outState->rawPointerData.pointerCount = outCount; + mPointerIdBits = newPointerIdBits; + + mMultiTouchMotionAccumulator.finishSync(); +} + +void MultiTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); + getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); + getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); + getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); + + if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && + mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { + size_t slotCount = mRawPointerAxes.slot.maxValue + 1; + if (slotCount > MAX_SLOTS) { + ALOGW("MultiTouch Device %s reported %zu slots but the framework " + "only supports a maximum of %zu slots at this time.", + getDeviceName().c_str(), slotCount, MAX_SLOTS); + slotCount = MAX_SLOTS; + } + mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); + } else { + mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, + false /*usingSlotsProtocol*/); + } +} + +bool MultiTouchInputMapper::hasStylus() const { + return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); +} + +// --- ExternalStylusInputMapper + +ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} + +uint32_t ExternalStylusInputMapper::getSources() { + return AINPUT_SOURCE_STYLUS; +} + +void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); +} + +void ExternalStylusInputMapper::dump(std::string& dump) { + dump += INDENT2 "External Stylus Input Mapper:\n"; + dump += INDENT3 "Raw Stylus Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); + dump += INDENT3 "Stylus State:\n"; + dumpStylusState(dump, mStylusState); +} + +void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); + mTouchButtonAccumulator.configure(getDevice()); +} + +void ExternalStylusInputMapper::reset(nsecs_t when) { + InputDevice* device = getDevice(); + mSingleTouchMotionAccumulator.reset(device); + mTouchButtonAccumulator.reset(device); + InputMapper::reset(when); +} + +void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { + mSingleTouchMotionAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void ExternalStylusInputMapper::sync(nsecs_t when) { + mStylusState.clear(); + + mStylusState.when = when; + + mStylusState.toolType = mTouchButtonAccumulator.getToolType(); + if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + + int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + if (mRawPressureAxis.valid) { + mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; + } else if (mTouchButtonAccumulator.isToolActive()) { + mStylusState.pressure = 1.0f; + } else { + mStylusState.pressure = 0.0f; + } + + mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); + + mContext->dispatchExternalStylusState(mStylusState); +} + +// --- JoystickInputMapper --- + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} + +JoystickInputMapper::~JoystickInputMapper() {} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (size_t i = 0; i < mAxes.size(); i++) { + const Axis& axis = mAxes.valueAt(i); + addMotionRange(axis.axisInfo.axis, axis, info); + + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + addMotionRange(axis.axisInfo.highAxis, axis, info); + } + } +} + +void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { + info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, + axis.resolution); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to register + * the old axes as duplicates of their corresponding new ones. */ + int32_t compatAxis = getCompatAxis(axisId); + if (compatAxis >= 0) { + info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, + axis.fuzz, axis.resolution); + } +} + +/* A mapping from axes the joystick actually has to the axes that should be + * artificially created for compatibility purposes. + * Returns -1 if no compatibility axis is needed. */ +int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_LTRIGGER: + return AMOTION_EVENT_AXIS_BRAKE; + case AMOTION_EVENT_AXIS_RTRIGGER: + return AMOTION_EVENT_AXIS_GAS; + } + return -1; +} + +void JoystickInputMapper::dump(std::string& dump) { + dump += INDENT2 "Joystick Input Mapper:\n"; + + dump += INDENT3 "Axes:\n"; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + const char* label = getAxisLabel(axis.axisInfo.axis); + if (label) { + dump += StringPrintf(INDENT4 "%s", label); + } else { + dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + label = getAxisLabel(axis.axisInfo.highAxis); + if (label) { + dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); + } else { + dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, + axis.axisInfo.splitValue); + } + } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { + dump += " (invert)"; + } + + dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " + "highScale=%0.5f, highOffset=%0.5f\n", + axis.scale, axis.offset, axis.highScale, axis.highOffset); + dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", + mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, + axis.rawAxisInfo.resolution); + } +} + +void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { + continue; // axis must be claimed by a different device + } + + RawAbsoluteAxisInfo rawAxisInfo; + getAbsoluteAxisInfo(abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + // Map axis. + AxisInfo axisInfo; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + } + + // Apply flat override. + int32_t rawFlat = + axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + // Calculate scaling factors and limits. + Axis axis; + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else if (isCenteredAxis(axisInfo.axis)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, + offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; + + mAxes.add(abs, axis); + } + } + + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", + getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axisInfo.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && + haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } + + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axisInfo.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } + } + } + } +} + +bool JoystickInputMapper::haveAxis(int32_t axisId) { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + if (axis.axisInfo.axis == axisId || + (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { + return true; + } + } + return false; +} + +void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { + size_t i = mAxes.size(); + while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { + if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + continue; + } + ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i); + } +} + +bool JoystickInputMapper::isCenteredAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_Z: + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + case AMOTION_EVENT_AXIS_HAT_X: + case AMOTION_EVENT_AXIS_HAT_Y: + case AMOTION_EVENT_AXIS_ORIENTATION: + case AMOTION_EVENT_AXIS_RUDDER: + case AMOTION_EVENT_AXIS_WHEEL: + return true; + default: + return false; + } +} + +void JoystickInputMapper::reset(nsecs_t when) { + // Recenter all axes. + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + axis.resetValue(); + } + + InputMapper::reset(when); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + ssize_t index = mAxes.indexOfKey(rawEvent->code); + if (index >= 0) { + Axis& axis = mAxes.editValueAt(index); + float newValue, highNewValue; + switch (axis.axisInfo.mode) { + case AxisInfo::MODE_INVERT: + newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + break; + case AxisInfo::MODE_SPLIT: + if (rawEvent->value < axis.axisInfo.splitValue) { + newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + } else if (rawEvent->value > axis.axisInfo.splitValue) { + newValue = 0.0f; + highNewValue = + (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + + axis.highOffset; + } else { + newValue = 0.0f; + highNewValue = 0.0f; + } + break; + default: + newValue = rawEvent->value * axis.scale + axis.offset; + highNewValue = 0.0f; + break; + } + axis.newValue = newValue; + axis.highNewValue = highNewValue; + } + break; + } + + case EV_SYN: + switch (rawEvent->code) { + case SYN_REPORT: + sync(rawEvent->when, false /*force*/); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when, bool force) { + if (!filterAxes(force)) { + return; + } + + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = 0; + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, + axis.highCurrentValue); + } + } + + // Moving a joystick axis should not wake the device because joysticks can + // be fairly noisy even when not in use. On the other hand, pushing a gamepad + // button will likely wake the device. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&args); +} + +void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, + float value) { + pointerCoords->setAxisValue(axis, value); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to produce + * values for the old axes as mirrors of the value of their corresponding + * new axes. */ + int32_t compatAxis = getCompatAxis(axis); + if (compatAxis >= 0) { + pointerCoords->setAxisValue(compatAxis, value); + } +} + +bool JoystickInputMapper::filterAxes(bool force) { + bool atLeastOneSignificantChange = force; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (force || + hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, + axis.max)) { + axis.currentValue = axis.newValue; + atLeastOneSignificantChange = true; + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + if (force || + hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, + axis.min, axis.max)) { + axis.highCurrentValue = axis.highNewValue; + atLeastOneSignificantChange = true; + } + } + } + return atLeastOneSignificantChange; +} + +bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, + float currentValue, float min, float max) { + if (newValue != currentValue) { + // Filter out small changes in value unless the value is converging on the axis + // bounds or center point. This is intended to reduce the amount of information + // sent to applications by particularly noisy joysticks (such as PS3). + if (fabs(newValue - currentValue) > filter || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { + return true; + } + } + return false; +} + +bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, + float thresholdValue) { + float newDistance = fabs(newValue - thresholdValue); + if (newDistance < filter) { + float oldDistance = fabs(currentValue - thresholdValue); + if (newDistance < oldDistance) { + return true; + } + } + return false; +} + +} // namespace android diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp new file mode 100644 index 0000000000..7c2311266c --- /dev/null +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputReaderFactory.h" +#include "InputReader.h" + +namespace android { + +sp createInputReader(const sp& policy, + const sp& listener) { + return new InputReader(std::make_unique(), policy, listener); +} + +} // namespace android \ No newline at end of file diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp new file mode 100644 index 0000000000..c075078528 --- /dev/null +++ b/services/inputflinger/reader/TouchVideoDevice.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TouchVideoDevice.h" + +#define LOG_TAG "TouchVideoDevice" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using android::base::StringPrintf; +using android::base::unique_fd; + +namespace android { + +TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, + uint32_t height, uint32_t width, + const std::array& readLocations) + : mFd(fd), + mName(std::move(name)), + mPath(std::move(devicePath)), + mHeight(height), + mWidth(width), + mReadLocations(readLocations) { + mFrames.reserve(MAX_QUEUE_SIZE); +}; + +std::unique_ptr TouchVideoDevice::create(std::string devicePath) { + unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK)); + if (fd.get() == INVALID_FD) { + ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno)); + return nullptr; + } + + struct v4l2_capability cap; + int result = ioctl(fd.get(), VIDIOC_QUERYCAP, &cap); + if (result == -1) { + ALOGE("VIDIOC_QUERYCAP failed: %s", strerror(errno)); + return nullptr; + } + if (!(cap.capabilities & V4L2_CAP_TOUCH)) { + ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. " + "Make sure device specifies V4L2_CAP_TOUCH"); + return nullptr; + } + ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", cap.driver, + cap.card, cap.bus_info, cap.version); + std::string name = reinterpret_cast(cap.card); + + struct v4l2_input v4l2_input_struct; + v4l2_input_struct.index = 0; + result = ioctl(fd.get(), VIDIOC_ENUMINPUT, &v4l2_input_struct); + if (result == -1) { + ALOGE("VIDIOC_ENUMINPUT failed: %s", strerror(errno)); + return nullptr; + } + + if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) { + ALOGE("Video device does not provide touch data. " + "Make sure device specifies V4L2_INPUT_TYPE_TOUCH."); + return nullptr; + } + + struct v4l2_format v4l2_fmt; + v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + result = ioctl(fd.get(), VIDIOC_G_FMT, &v4l2_fmt); + if (result == -1) { + ALOGE("VIDIOC_G_FMT failed: %s", strerror(errno)); + return nullptr; + } + const uint32_t height = v4l2_fmt.fmt.pix.height; + const uint32_t width = v4l2_fmt.fmt.pix.width; + ALOGI("Frame dimensions: height = %" PRIu32 " width = %" PRIu32, height, width); + + struct v4l2_requestbuffers req = {}; + req.count = NUM_BUFFERS; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + // req.reserved is zeroed during initialization, which is required per v4l docs + result = ioctl(fd.get(), VIDIOC_REQBUFS, &req); + if (result == -1) { + ALOGE("VIDIOC_REQBUFS failed: %s", strerror(errno)); + return nullptr; + } + if (req.count != NUM_BUFFERS) { + ALOGE("Requested %zu buffers, but driver responded with count=%i", NUM_BUFFERS, req.count); + return nullptr; + } + + struct v4l2_buffer buf = {}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + // buf.reserved and buf.reserved2 are zeroed during initialization, required per v4l docs + std::array readLocations; + for (size_t i = 0; i < NUM_BUFFERS; i++) { + buf.index = i; + result = ioctl(fd.get(), VIDIOC_QUERYBUF, &buf); + if (result == -1) { + ALOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno)); + return nullptr; + } + if (buf.length != height * width * sizeof(int16_t)) { + ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", buf.length, + buf.m.offset); + return nullptr; + } + + readLocations[i] = static_cast( + mmap(nullptr /* start anywhere */, buf.length, PROT_READ /* required */, + MAP_SHARED /* recommended */, fd.get(), buf.m.offset)); + if (readLocations[i] == MAP_FAILED) { + ALOGE("%s: map failed: %s", __func__, strerror(errno)); + return nullptr; + } + } + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + result = ioctl(fd.get(), VIDIOC_STREAMON, &type); + if (result == -1) { + ALOGE("VIDIOC_STREAMON failed: %s", strerror(errno)); + return nullptr; + } + + for (size_t i = 0; i < NUM_BUFFERS; i++) { + buf.index = i; + result = ioctl(fd.get(), VIDIOC_QBUF, &buf); + if (result == -1) { + ALOGE("VIDIOC_QBUF failed for buffer %zu: %s", i, strerror(errno)); + return nullptr; + } + } + // Using 'new' to access a non-public constructor. + return std::unique_ptr(new TouchVideoDevice(fd.release(), std::move(name), + std::move(devicePath), height, + width, readLocations)); +} + +size_t TouchVideoDevice::readAndQueueFrames() { + std::vector frames = readFrames(); + const size_t numFrames = frames.size(); + if (numFrames == 0) { + // Likely an error occurred + return 0; + } + // Concatenate the vectors, then clip up to maximum size allowed + mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()), + std::make_move_iterator(frames.end())); + if (mFrames.size() > MAX_QUEUE_SIZE) { + ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE, + mFrames.size() - MAX_QUEUE_SIZE); + mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE); + } + return numFrames; +} + +std::vector TouchVideoDevice::consumeFrames() { + std::vector frames = std::move(mFrames); + mFrames = {}; + return frames; +} + +std::optional TouchVideoDevice::readFrame() { + struct v4l2_buffer buf = {}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + int result = ioctl(mFd.get(), VIDIOC_DQBUF, &buf); + if (result == -1) { + // EAGAIN means we've reached the end of the read buffer, so it's expected. + if (errno != EAGAIN) { + ALOGE("VIDIOC_DQBUF failed: %s", strerror(errno)); + } + return std::nullopt; + } + if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) { + // We use CLOCK_MONOTONIC for input events, so if the clocks don't match, + // we can't compare timestamps. Just log a warning, since this is a driver issue + ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec, + buf.timestamp.tv_usec); + } + std::vector data(mHeight * mWidth); + const int16_t* readFrom = mReadLocations[buf.index]; + std::copy(readFrom, readFrom + mHeight * mWidth, data.begin()); + TouchVideoFrame frame(mHeight, mWidth, std::move(data), buf.timestamp); + + result = ioctl(mFd.get(), VIDIOC_QBUF, &buf); + if (result == -1) { + ALOGE("VIDIOC_QBUF failed: %s", strerror(errno)); + } + return std::make_optional(std::move(frame)); +} + +/* + * This function should not be called unless buffer is ready! This must be checked with + * select, poll, epoll, or some other similar api first. + * The oldest frame will be at the beginning of the array. + */ +std::vector TouchVideoDevice::readFrames() { + std::vector frames; + while (true) { + std::optional frame = readFrame(); + if (!frame) { + break; + } + frames.push_back(std::move(*frame)); + } + return frames; +} + +TouchVideoDevice::~TouchVideoDevice() { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int result = ioctl(mFd.get(), VIDIOC_STREAMOFF, &type); + if (result == -1) { + ALOGE("VIDIOC_STREAMOFF failed: %s", strerror(errno)); + } + for (const int16_t* buffer : mReadLocations) { + void* bufferAddress = static_cast(const_cast(buffer)); + result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t)); + if (result == -1) { + ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno)); + } + } +} + +std::string TouchVideoDevice::dump() const { + return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32 + ", fd=%i, hasValidFd=%s", + mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(), + hasValidFd() ? "true" : "false"); +} + +} // namespace android diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h new file mode 100644 index 0000000000..da0f497b90 --- /dev/null +++ b/services/inputflinger/reader/include/EventHub.h @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +#ifndef _RUNTIME_EVENT_HUB_H +#define _RUNTIME_EVENT_HUB_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "TouchVideoDevice.h" + +/* Convenience constants. */ + +#define BTN_FIRST 0x100 // first button code +#define BTN_LAST 0x15f // last button code + +namespace android { + +/* + * A raw event as retrieved from the EventHub. + */ +struct RawEvent { + nsecs_t when; + int32_t deviceId; + int32_t type; + int32_t code; + int32_t value; +}; + +/* Describes an absolute axis. */ +struct RawAbsoluteAxisInfo { + bool valid; // true if the information is valid, false otherwise + + int32_t minValue; // minimum value + int32_t maxValue; // maximum value + int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 + int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise + int32_t resolution; // resolution in units per mm or radians per mm + + inline void clear() { + valid = false; + minValue = 0; + maxValue = 0; + flat = 0; + fuzz = 0; + resolution = 0; + } +}; + +/* + * Input device classes. + */ +enum { + /* The input device is a keyboard or has buttons. */ + INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, + + /* The input device is an alpha-numeric keyboard (not just a dial pad). */ + INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, + + /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ + INPUT_DEVICE_CLASS_TOUCH = 0x00000004, + + /* The input device is a cursor device such as a trackball or mouse. */ + INPUT_DEVICE_CLASS_CURSOR = 0x00000008, + + /* The input device is a multi-touch touchscreen. */ + INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, + + /* The input device is a directional pad (implies keyboard, has DPAD keys). */ + INPUT_DEVICE_CLASS_DPAD = 0x00000020, + + /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ + INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + + /* The input device has switches. */ + INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + + /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, + + /* The input device has a vibrator (supports FF_RUMBLE). */ + INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, + + /* The input device has a microphone. */ + INPUT_DEVICE_CLASS_MIC = 0x00000400, + + /* The input device is an external stylus (has data we want to fuse with touch data). */ + INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, + + /* The input device has a rotary encoder */ + INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000, + + /* The input device is virtual (not a real device, not part of UI configuration). */ + INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, + + /* The input device is external (not built-in). */ + INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, +}; + +/* + * Gets the class that owns an axis, in cases where multiple classes might claim + * the same axis for different purposes. + */ +extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); + +/* + * Grand Central Station for events. + * + * The event hub aggregates input events received across all known input + * devices on the system, including devices that may be emulated by the simulator + * environment. In addition, the event hub generates fake input events to indicate + * when devices are added or removed. + * + * The event hub provides a stream of input events (via the getEvent function). + * It also supports querying the current actual state of input devices such as identifying + * which keys are currently down. Finally, the event hub keeps track of the capabilities of + * individual input devices, such as their class and the set of key codes that they support. + */ +class EventHubInterface { +public: + EventHubInterface() {} + virtual ~EventHubInterface() {} + + // Synthetic raw event type codes produced when devices are added or removed. + enum { + // Sent when a device is added. + DEVICE_ADDED = 0x10000000, + // Sent when a device is removed. + DEVICE_REMOVED = 0x20000000, + // Sent when all added/removed devices from the most recent scan have been reported. + // This event is always sent at least once. + FINISHED_DEVICE_SCAN = 0x30000000, + + FIRST_SYNTHETIC_EVENT = DEVICE_ADDED, + }; + + virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; + + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0; + + virtual int32_t getDeviceControllerNumber(int32_t deviceId) const = 0; + + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; + + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const = 0; + + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; + + virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; + + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const = 0; + + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const = 0; + + // Sets devices that are excluded from opening. + // This can be used to ignore input devices for sensors. + virtual void setExcludedDevices(const std::vector& devices) = 0; + + /* + * Wait for events to become available and returns them. + * After returning, the EventHub holds onto a wake lock until the next call to getEvent. + * This ensures that the device will not go to sleep while the event is being processed. + * If the device needs to remain awake longer than that, then the caller is responsible + * for taking care of it (say, by poking the power manager user activity timer). + * + * The timeout is advisory only. If the device is asleep, it will not wake just to + * service the timeout. + * + * Returns the number of events obtained, or 0 if the timeout expired. + */ + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0; + virtual std::vector getVideoFrames(int32_t deviceId) = 0; + + /* + * Query current input state. + */ + virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; + virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; + virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const = 0; + + /* + * Examine key input devices for specific framework keycode support + */ + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const = 0; + + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; + + /* LED related functions expect Android LED constants, not scan codes or HID usages */ + virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; + virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; + + virtual void getVirtualKeyDefinitions( + int32_t deviceId, std::vector& outVirtualKeys) const = 0; + + virtual sp getKeyCharacterMap(int32_t deviceId) const = 0; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map) = 0; + + /* Control the vibrator. */ + virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0; + virtual void cancelVibrate(int32_t deviceId) = 0; + + /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ + virtual void requestReopenDevices() = 0; + + /* Wakes up getEvents() if it is blocked on a read. */ + virtual void wake() = 0; + + /* Dump EventHub state to a string. */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the reader has not deadlocked. */ + virtual void monitor() = 0; + + /* Return true if the device is enabled. */ + virtual bool isDeviceEnabled(int32_t deviceId) = 0; + + /* Enable an input device */ + virtual status_t enableDevice(int32_t deviceId) = 0; + + /* Disable an input device. Closes file descriptor to that device. */ + virtual status_t disableDevice(int32_t deviceId) = 0; +}; + +class EventHub : public EventHubInterface { +public: + EventHub(); + + virtual uint32_t getDeviceClasses(int32_t deviceId) const; + + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const; + + virtual int32_t getDeviceControllerNumber(int32_t deviceId) const; + + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; + + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const; + + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; + + virtual bool hasInputProperty(int32_t deviceId, int property) const; + + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const; + + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; + + virtual void setExcludedDevices(const std::vector& devices); + + virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; + virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; + virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; + + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const; + + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); + virtual std::vector getVideoFrames(int32_t deviceId); + + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const; + virtual bool hasLed(int32_t deviceId, int32_t led) const; + virtual void setLedState(int32_t deviceId, int32_t led, bool on); + + virtual void getVirtualKeyDefinitions(int32_t deviceId, + std::vector& outVirtualKeys) const; + + virtual sp getKeyCharacterMap(int32_t deviceId) const; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map); + + virtual void vibrate(int32_t deviceId, nsecs_t duration); + virtual void cancelVibrate(int32_t deviceId); + + virtual void requestReopenDevices(); + + virtual void wake(); + + virtual void dump(std::string& dump); + virtual void monitor(); + + virtual ~EventHub(); + +private: + struct Device { + Device* next; + + int fd; // may be -1 if device is closed + const int32_t id; + const std::string path; + const InputDeviceIdentifier identifier; + + std::unique_ptr videoDevice; + + uint32_t classes; + + uint8_t keyBitmask[(KEY_MAX + 1) / 8]; + uint8_t absBitmask[(ABS_MAX + 1) / 8]; + uint8_t relBitmask[(REL_MAX + 1) / 8]; + uint8_t swBitmask[(SW_MAX + 1) / 8]; + uint8_t ledBitmask[(LED_MAX + 1) / 8]; + uint8_t ffBitmask[(FF_MAX + 1) / 8]; + uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; + + std::string configurationFile; + PropertyMap* configuration; + std::unique_ptr virtualKeyMap; + KeyMap keyMap; + + sp overlayKeyMap; + sp combinedKeyMap; + + bool ffEffectPlaying; + int16_t ffEffectId; // initially -1 + + int32_t controllerNumber; + + Device(int fd, int32_t id, const std::string& path, + const InputDeviceIdentifier& identifier); + ~Device(); + + void close(); + + bool enabled; // initially true + status_t enable(); + status_t disable(); + bool hasValidFd(); + const bool isVirtual; // set if fd < 0 is passed to constructor + + const sp& getKeyCharacterMap() const { + if (combinedKeyMap != nullptr) { + return combinedKeyMap; + } + return keyMap.keyCharacterMap; + } + }; + + status_t openDeviceLocked(const char* devicePath); + void openVideoDeviceLocked(const std::string& devicePath); + void createVirtualKeyboardLocked(); + void addDeviceLocked(Device* device); + void assignDescriptorLocked(InputDeviceIdentifier& identifier); + + void closeDeviceByPathLocked(const char* devicePath); + void closeVideoDeviceByPathLocked(const std::string& devicePath); + void closeDeviceLocked(Device* device); + void closeAllDevicesLocked(); + + void configureFd(Device* device); + + bool isDeviceEnabled(int32_t deviceId); + status_t enableDevice(int32_t deviceId); + status_t disableDevice(int32_t deviceId); + status_t registerFdForEpoll(int fd); + status_t unregisterFdFromEpoll(int fd); + status_t registerDeviceForEpollLocked(Device* device); + void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice); + status_t unregisterDeviceFromEpollLocked(Device* device); + void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice); + + status_t scanDirLocked(const char* dirname); + status_t scanVideoDirLocked(const std::string& dirname); + void scanDevicesLocked(); + status_t readNotifyLocked(); + + Device* getDeviceByDescriptorLocked(const std::string& descriptor) const; + Device* getDeviceLocked(int32_t deviceId) const; + Device* getDeviceByPathLocked(const char* devicePath) const; + /** + * Look through all available fd's (both for input devices and for video devices), + * and return the device pointer. + */ + Device* getDeviceByFdLocked(int fd) const; + + bool hasKeycodeLocked(Device* device, int keycode) const; + + void loadConfigurationLocked(Device* device); + bool loadVirtualKeyMapLocked(Device* device); + status_t loadKeyMapLocked(Device* device); + + bool isExternalDeviceLocked(Device* device); + bool deviceHasMicLocked(Device* device); + + int32_t getNextControllerNumberLocked(Device* device); + void releaseControllerNumberLocked(Device* device); + void setLedForControllerLocked(Device* device); + + status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const; + void setLedStateLocked(Device* device, int32_t led, bool on); + + // Protect all internal state. + mutable Mutex mLock; + + // The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none. + // EventHub remaps the built-in keyboard to id 0 externally as required by the API. + enum { + // Must not conflict with any other assigned device ids, including + // the virtual keyboard id (-1). + NO_BUILT_IN_KEYBOARD = -2, + }; + int32_t mBuiltInKeyboardId; + + int32_t mNextDeviceId; + + BitSet32 mControllerNumbers; + + KeyedVector mDevices; + /** + * Video devices that report touchscreen heatmap, but have not (yet) been paired + * with a specific input device. Video device discovery is independent from input device + * discovery, so the two types of devices could be found in any order. + * Ideally, video devices in this queue do not have an open fd, or at least aren't + * actively streaming. + */ + std::vector> mUnattachedVideoDevices; + + Device* mOpeningDevices; + Device* mClosingDevices; + + bool mNeedToSendFinishedDeviceScan; + bool mNeedToReopenDevices; + bool mNeedToScanDevices; + std::vector mExcludedDevices; + + int mEpollFd; + int mINotifyFd; + int mWakeReadPipeFd; + int mWakeWritePipeFd; + + int mInputWd; + int mVideoWd; + + // Maximum number of signalled FDs to handle at a time. + static const int EPOLL_MAX_EVENTS = 16; + + // The array of pending epoll events and the index of the next event to be handled. + struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; + size_t mPendingEventCount; + size_t mPendingEventIndex; + bool mPendingINotify; +}; + +}; // namespace android + +#endif // _RUNTIME_EVENT_HUB_H diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h new file mode 100644 index 0000000000..1b8177c618 --- /dev/null +++ b/services/inputflinger/reader/include/InputReader.h @@ -0,0 +1,1706 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_READER_H +#define _UI_INPUT_READER_H + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderBase.h" +#include "PointerControllerInterface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +class InputDevice; +class InputMapper; + +struct StylusState { + /* Time the stylus event was received. */ + nsecs_t when; + /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ + float pressure; + /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ + uint32_t buttons; + /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ + int32_t toolType; + + void copyFrom(const StylusState& other) { + when = other.when; + pressure = other.pressure; + buttons = other.buttons; + toolType = other.toolType; + } + + void clear() { + when = LLONG_MAX; + pressure = 0.f; + buttons = 0; + toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + } +}; + +/* Internal interface used by individual input devices to access global input device state + * and parameters maintained by the input reader. + */ +class InputReaderContext { +public: + InputReaderContext() {} + virtual ~InputReaderContext() {} + + virtual void updateGlobalMetaState() = 0; + virtual int32_t getGlobalMetaState() = 0; + + virtual void disableVirtualKeysUntil(nsecs_t time) = 0; + virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode) = 0; + + virtual void fadePointer() = 0; + + virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual int32_t bumpGeneration() = 0; + + virtual void getExternalStylusDevices(std::vector& outDevices) = 0; + virtual void dispatchExternalStylusState(const StylusState& outState) = 0; + + virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputListenerInterface* getListener() = 0; + virtual EventHubInterface* getEventHub() = 0; + + virtual uint32_t getNextSequenceNum() = 0; +}; + +/* The input reader reads raw event data from the event hub and processes it into input events + * that it sends to the input listener. Some functions of the input reader, such as early + * event filtering in low power states, are controlled by a separate policy object. + * + * The InputReader owns a collection of InputMappers. Most of the work it does happens + * on the input reader thread but the InputReader can receive queries from other system + * components running on arbitrary threads. To keep things manageable, the InputReader + * uses a single Mutex to guard its state. The Mutex may be held while calling into the + * EventHub or the InputReaderPolicy but it is never held while calling into the + * InputListener. + */ +class InputReader : public InputReaderInterface { +public: + InputReader(std::shared_ptr eventHub, + const sp& policy, + const sp& listener); + virtual ~InputReader(); + + virtual void dump(std::string& dump); + virtual void monitor(); + + virtual void loopOnce(); + + virtual void getInputDevices(std::vector& outInputDevices); + + virtual bool isInputDeviceEnabled(int32_t deviceId); + + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw); + + virtual void toggleCapsLockState(int32_t deviceId); + + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void requestRefreshConfiguration(uint32_t changes); + + virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t deviceId, int32_t token); + + virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); + +protected: + // These members are protected so they can be instrumented by test cases. + virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes); + + class ContextImpl : public InputReaderContext { + InputReader* mReader; + + public: + explicit ContextImpl(InputReader* reader); + + virtual void updateGlobalMetaState(); + virtual int32_t getGlobalMetaState(); + virtual void disableVirtualKeysUntil(nsecs_t time); + virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode); + virtual void fadePointer(); + virtual void requestTimeoutAtTime(nsecs_t when); + virtual int32_t bumpGeneration(); + virtual void getExternalStylusDevices(std::vector& outDevices); + virtual void dispatchExternalStylusState(const StylusState& outState); + virtual InputReaderPolicyInterface* getPolicy(); + virtual InputListenerInterface* getListener(); + virtual EventHubInterface* getEventHub(); + virtual uint32_t getNextSequenceNum(); + } mContext; + + friend class ContextImpl; + +private: + Mutex mLock; + + Condition mReaderIsAliveCondition; + + // This could be unique_ptr, but due to the way InputReader tests are written, + // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test + // in parallel to passing it to the InputReader. + std::shared_ptr mEventHub; + sp mPolicy; + sp mQueuedListener; + + InputReaderConfiguration mConfig; + + // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers + uint32_t mNextSequenceNum; + + // The event queue. + static const int EVENT_BUFFER_SIZE = 256; + RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; + + KeyedVector mDevices; + + // low-level input event decoding and device management + void processEventsLocked(const RawEvent* rawEvents, size_t count); + + void addDeviceLocked(nsecs_t when, int32_t deviceId); + void removeDeviceLocked(nsecs_t when, int32_t deviceId); + void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); + void timeoutExpiredLocked(nsecs_t when); + + void handleConfigurationChangedLocked(nsecs_t when); + + int32_t mGlobalMetaState; + void updateGlobalMetaStateLocked(); + int32_t getGlobalMetaStateLocked(); + + void notifyExternalStylusPresenceChanged(); + void getExternalStylusDevicesLocked(std::vector& outDevices); + void dispatchExternalStylusState(const StylusState& state); + + void fadePointerLocked(); + + int32_t mGeneration; + int32_t bumpGenerationLocked(); + + void getInputDevicesLocked(std::vector& outInputDevices); + + nsecs_t mDisableVirtualKeysTimeout; + void disableVirtualKeysUntilLocked(nsecs_t time); + bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode); + + nsecs_t mNextTimeout; + void requestTimeoutAtTimeLocked(nsecs_t when); + + uint32_t mConfigurationChangesToRefresh; + void refreshConfigurationLocked(uint32_t changes); + + // state queries + typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc); + bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); +}; + +/* Represents the state of a single input device. */ +class InputDevice { +public: + InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes); + ~InputDevice(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() const { return mId; } + inline int32_t getControllerNumber() const { return mControllerNumber; } + inline int32_t getGeneration() const { return mGeneration; } + inline const std::string getName() const { return mIdentifier.name; } + inline const std::string getDescriptor() { return mIdentifier.descriptor; } + inline uint32_t getClasses() const { return mClasses; } + inline uint32_t getSources() const { return mSources; } + + inline bool isExternal() { return mIsExternal; } + inline void setExternal(bool external) { mIsExternal = external; } + inline std::optional getAssociatedDisplayPort() const { + return mAssociatedDisplayPort; + } + inline std::optional getAssociatedViewport() const { + return mAssociatedViewport; + } + inline void setMic(bool hasMic) { mHasMic = hasMic; } + inline bool hasMic() const { return mHasMic; } + + inline bool isIgnored() { return mMappers.empty(); } + + bool isEnabled(); + void setEnabled(bool enabled, nsecs_t when); + + void dump(std::string& dump); + void addMapper(InputMapper* mapper); + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + void reset(nsecs_t when); + void process(const RawEvent* rawEvents, size_t count); + void timeoutExpired(nsecs_t when); + void updateExternalStylusState(const StylusState& state); + + void getDeviceInfo(InputDeviceInfo* outDeviceInfo); + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags); + void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + void cancelVibrate(int32_t token); + void cancelTouch(nsecs_t when); + + int32_t getMetaState(); + void updateMetaState(int32_t keyCode); + + void fadePointer(); + + void bumpGeneration(); + + void notifyReset(nsecs_t when); + + inline const PropertyMap& getConfiguration() { return mConfiguration; } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); } + + bool hasAbsoluteAxis(int32_t code) { + RawAbsoluteAxisInfo info; + getEventHub()->getAbsoluteAxisInfo(mId, code, &info); + return info.valid; + } + + bool isKeyPressed(int32_t code) { + return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + } + + int32_t getAbsoluteAxisValue(int32_t code) { + int32_t value; + getEventHub()->getAbsoluteAxisValue(mId, code, &value); + return value; + } + + std::optional getAssociatedDisplayId(); + +private: + InputReaderContext* mContext; + int32_t mId; + int32_t mGeneration; + int32_t mControllerNumber; + InputDeviceIdentifier mIdentifier; + std::string mAlias; + uint32_t mClasses; + + std::vector mMappers; + + uint32_t mSources; + bool mIsExternal; + std::optional mAssociatedDisplayPort; + std::optional mAssociatedViewport; + bool mHasMic; + bool mDropUntilNextSync; + + typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); + + PropertyMap mConfiguration; +}; + +/* Keeps track of the state of mouse or touch pad buttons. */ +class CursorButtonAccumulator { +public: + CursorButtonAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + +private: + bool mBtnLeft; + bool mBtnRight; + bool mBtnMiddle; + bool mBtnBack; + bool mBtnSide; + bool mBtnForward; + bool mBtnExtra; + bool mBtnTask; + + void clearButtons(); +}; + +/* Keeps track of cursor movements. */ + +class CursorMotionAccumulator { +public: + CursorMotionAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + +private: + int32_t mRelX; + int32_t mRelY; + + void clearRelativeAxes(); +}; + +/* Keeps track of cursor scrolling motions. */ + +class CursorScrollAccumulator { +public: + CursorScrollAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline bool haveRelativeVWheel() const { return mHaveRelWheel; } + inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + inline int32_t getRelativeVWheel() const { return mRelWheel; } + inline int32_t getRelativeHWheel() const { return mRelHWheel; } + +private: + bool mHaveRelWheel; + bool mHaveRelHWheel; + + int32_t mRelX; + int32_t mRelY; + int32_t mRelWheel; + int32_t mRelHWheel; + + void clearRelativeAxes(); +}; + +/* Keeps track of the state of touch, stylus and tool buttons. */ +class TouchButtonAccumulator { +public: + TouchButtonAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + int32_t getToolType() const; + bool isToolActive() const; + bool isHovering() const; + bool hasStylus() const; + +private: + bool mHaveBtnTouch; + bool mHaveStylus; + + bool mBtnTouch; + bool mBtnStylus; + bool mBtnStylus2; + bool mBtnToolFinger; + bool mBtnToolPen; + bool mBtnToolRubber; + bool mBtnToolBrush; + bool mBtnToolPencil; + bool mBtnToolAirbrush; + bool mBtnToolMouse; + bool mBtnToolLens; + bool mBtnToolDoubleTap; + bool mBtnToolTripleTap; + bool mBtnToolQuadTap; + + void clearButtons(); +}; + +/* Raw axis information from the driver. */ +struct RawPointerAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + RawAbsoluteAxisInfo distance; + RawAbsoluteAxisInfo tiltX; + RawAbsoluteAxisInfo tiltY; + RawAbsoluteAxisInfo trackingId; + RawAbsoluteAxisInfo slot; + + RawPointerAxes(); + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } + inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } + void clear(); +}; + +/* Raw data for a collection of pointers including a pointer id mapping table. */ +struct RawPointerData { + struct Pointer { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + int32_t distance; + int32_t tiltX; + int32_t tiltY; + int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant + bool isHovering; + }; + + uint32_t pointerCount; + Pointer pointers[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + RawPointerData(); + void clear(); + void copyFrom(const RawPointerData& other); + void getCentroidOfTouchingPointers(float* outX, float* outY) const; + + inline void markIdBit(uint32_t id, bool isHovering) { + if (isHovering) { + hoveringIdBits.markBit(id); + } else { + touchingIdBits.markBit(id); + } + } + + inline void clearIdBits() { + hoveringIdBits.clear(); + touchingIdBits.clear(); + } + + inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } + + inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } +}; + +/* Cooked data for a collection of pointers including a pointer id mapping table. */ +struct CookedPointerData { + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + CookedPointerData(); + void clear(); + void copyFrom(const CookedPointerData& other); + + inline const PointerCoords& pointerCoordsForId(uint32_t id) const { + return pointerCoords[idToIndex[id]]; + } + + inline PointerCoords& editPointerCoordsWithId(uint32_t id) { + return pointerCoords[idToIndex[id]]; + } + + inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { + return pointerProperties[idToIndex[id]]; + } + + inline bool isHovering(uint32_t pointerIndex) const { + return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); + } + + inline bool isTouching(uint32_t pointerIndex) const { + return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); + } +}; + +/* Keeps track of the state of single-touch protocol. */ +class SingleTouchMotionAccumulator { +public: + SingleTouchMotionAccumulator(); + + void process(const RawEvent* rawEvent); + void reset(InputDevice* device); + + inline int32_t getAbsoluteX() const { return mAbsX; } + inline int32_t getAbsoluteY() const { return mAbsY; } + inline int32_t getAbsolutePressure() const { return mAbsPressure; } + inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } + inline int32_t getAbsoluteDistance() const { return mAbsDistance; } + inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } + inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } + +private: + int32_t mAbsX; + int32_t mAbsY; + int32_t mAbsPressure; + int32_t mAbsToolWidth; + int32_t mAbsDistance; + int32_t mAbsTiltX; + int32_t mAbsTiltY; + + void clearAbsoluteAxes(); +}; + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMTPositionX; } + inline int32_t getY() const { return mAbsMTPositionY; } + inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMTOrientation; } + inline int32_t getTrackingId() const { return mAbsMTTrackingId; } + inline int32_t getPressure() const { return mAbsMTPressure; } + inline int32_t getDistance() const { return mAbsMTDistance; } + inline int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse; + bool mHaveAbsMTTouchMinor; + bool mHaveAbsMTWidthMinor; + bool mHaveAbsMTToolType; + + int32_t mAbsMTPositionX; + int32_t mAbsMTPositionY; + int32_t mAbsMTTouchMajor; + int32_t mAbsMTTouchMinor; + int32_t mAbsMTWidthMajor; + int32_t mAbsMTWidthMinor; + int32_t mAbsMTOrientation; + int32_t mAbsMTTrackingId; + int32_t mAbsMTPressure; + int32_t mAbsMTDistance; + int32_t mAbsMTToolType; + + Slot(); + void clear(); + }; + + MultiTouchMotionAccumulator(); + ~MultiTouchMotionAccumulator(); + + void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); + void reset(InputDevice* device); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlotCount; } + inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } + +private: + int32_t mCurrentSlot; + Slot* mSlots; + size_t mSlotCount; + bool mUsingSlotsProtocol; + bool mHaveStylus; + + void clearSlots(int32_t initialSlot); +}; + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + * + * InputMapper lifecycle: + * - create + * - configure with 0 changes + * - reset + * - process, process, process (may occasionally reconfigure with non-zero changes or reset) + * - reset + * - destroy + */ +class InputMapper { +public: + explicit InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const std::string getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputListenerInterface* getListener() { return mContext->getListener(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent) = 0; + virtual void timeoutExpired(nsecs_t when); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void cancelTouch(nsecs_t when); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + + virtual void updateExternalStylusState(const StylusState& state); + + virtual void fadePointer(); + virtual std::optional getAssociatedDisplayId() { return std::nullopt; } + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); + + static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name); + static void dumpStylusState(std::string& dump, const StylusState& state); +}; + +class SwitchInputMapper : public InputMapper { +public: + explicit SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual void dump(std::string& dump); + +private: + uint32_t mSwitchValues; + uint32_t mUpdatedSwitchMask; + + void processSwitch(int32_t switchCode, int32_t switchValue); + void sync(nsecs_t when); +}; + +class VibratorInputMapper : public InputMapper { +public: + explicit VibratorInputMapper(InputDevice* device); + virtual ~VibratorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void process(const RawEvent* rawEvent); + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void timeoutExpired(nsecs_t when); + virtual void dump(std::string& dump); + +private: + bool mVibrating; + nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; + size_t mPatternSize; + ssize_t mRepeat; + int32_t mToken; + ssize_t mIndex; + nsecs_t mNextStepTime; + + void nextStep(); + void stopVibrating(); +}; + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + virtual std::optional getAssociatedDisplayId(); + +private: + // The current viewport. + std::optional mViewport; + + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + uint32_t mSource; + int32_t mKeyboardType; + + std::vector mKeyDowns; // keys that are down + int32_t mMetaState; + nsecs_t mDownTime; // time of most recent key down + + int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + + struct LedState { + bool avail; // led is available + bool on; // we think the led is currently on + }; + LedState mCapsLockLedState; + LedState mNumLockLedState; + LedState mScrollLockLedState; + + // Immutable configuration parameters. + struct Parameters { + bool orientationAware; + bool handlesKeyRepeat; + } mParameters; + + void configureParameters(); + void dumpParameters(std::string& dump); + + int32_t getOrientation(); + int32_t getDisplayId(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + bool isMediaKey(int32_t keyCode); + + void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); + + bool updateMetaStateIfNeeded(int32_t keyCode, bool down); + + ssize_t findKeyDown(int32_t scanCode); + + void resetLedState(); + void initializeLedState(LedState& ledState, int32_t led); + void updateLedState(bool reset); + void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); + std::optional findViewport(nsecs_t when, + const InputReaderConfiguration* config); +}; + +class CursorInputMapper : public InputMapper { +public: + explicit CursorInputMapper(InputDevice* device); + virtual ~CursorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + + virtual void fadePointer(); + + virtual std::optional getAssociatedDisplayId(); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + // Immutable configuration parameters. + struct Parameters { + enum Mode { + MODE_POINTER, + MODE_POINTER_RELATIVE, + MODE_NAVIGATION, + }; + + Mode mode; + bool hasAssociatedDisplay; + bool orientationAware; + } mParameters; + + CursorButtonAccumulator mCursorButtonAccumulator; + CursorMotionAccumulator mCursorMotionAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + + int32_t mSource; + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + float mVWheelScale; + float mHWheelScale; + + // Velocity controls for mouse pointer and wheel movements. + // The controls for X and Y wheel movements are separate to keep them decoupled. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + int32_t mOrientation; + + sp mPointerController; + + int32_t mButtonState; + nsecs_t mDownTime; + + void configureParameters(); + void dumpParameters(std::string& dump); + + void sync(nsecs_t when); +}; + +class RotaryEncoderInputMapper : public InputMapper { +public: + explicit RotaryEncoderInputMapper(InputDevice* device); + virtual ~RotaryEncoderInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + CursorScrollAccumulator mRotaryEncoderScrollAccumulator; + + int32_t mSource; + float mScalingFactor; + int32_t mOrientation; + + void sync(nsecs_t when); +}; + +class TouchInputMapper : public InputMapper { +public: + explicit TouchInputMapper(InputDevice* device); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void fadePointer(); + virtual void cancelTouch(nsecs_t when); + virtual void timeoutExpired(nsecs_t when); + virtual void updateExternalStylusState(const StylusState& state); + virtual std::optional getAssociatedDisplayId(); + +protected: + CursorButtonAccumulator mCursorButtonAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + // Input sources and device mode. + uint32_t mSource; + + enum DeviceMode { + DEVICE_MODE_DISABLED, // input is disabled + DEVICE_MODE_DIRECT, // direct mapping (touchscreen) + DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) + DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) + DEVICE_MODE_POINTER, // pointer mapping (pointer) + }; + DeviceMode mDeviceMode; + + // The reader's configuration. + InputReaderConfiguration mConfig; + + // Immutable configuration parameters. + struct Parameters { + enum DeviceType { + DEVICE_TYPE_TOUCH_SCREEN, + DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_TOUCH_NAVIGATION, + DEVICE_TYPE_POINTER, + }; + + DeviceType deviceType; + bool hasAssociatedDisplay; + bool associatedDisplayIsExternal; + bool orientationAware; + bool hasButtonUnderPad; + std::string uniqueDisplayId; + + enum GestureMode { + GESTURE_MODE_SINGLE_TOUCH, + GESTURE_MODE_MULTI_TOUCH, + }; + GestureMode gestureMode; + + bool wake; + } mParameters; + + // Immutable calibration parameters in parsed form. + struct Calibration { + // Size + enum SizeCalibration { + SIZE_CALIBRATION_DEFAULT, + SIZE_CALIBRATION_NONE, + SIZE_CALIBRATION_GEOMETRIC, + SIZE_CALIBRATION_DIAMETER, + SIZE_CALIBRATION_BOX, + SIZE_CALIBRATION_AREA, + }; + + SizeCalibration sizeCalibration; + + bool haveSizeScale; + float sizeScale; + bool haveSizeBias; + float sizeBias; + bool haveSizeIsSummed; + bool sizeIsSummed; + + // Pressure + enum PressureCalibration { + PRESSURE_CALIBRATION_DEFAULT, + PRESSURE_CALIBRATION_NONE, + PRESSURE_CALIBRATION_PHYSICAL, + PRESSURE_CALIBRATION_AMPLITUDE, + }; + + PressureCalibration pressureCalibration; + bool havePressureScale; + float pressureScale; + + // Orientation + enum OrientationCalibration { + ORIENTATION_CALIBRATION_DEFAULT, + ORIENTATION_CALIBRATION_NONE, + ORIENTATION_CALIBRATION_INTERPOLATED, + ORIENTATION_CALIBRATION_VECTOR, + }; + + OrientationCalibration orientationCalibration; + + // Distance + enum DistanceCalibration { + DISTANCE_CALIBRATION_DEFAULT, + DISTANCE_CALIBRATION_NONE, + DISTANCE_CALIBRATION_SCALED, + }; + + DistanceCalibration distanceCalibration; + bool haveDistanceScale; + float distanceScale; + + enum CoverageCalibration { + COVERAGE_CALIBRATION_DEFAULT, + COVERAGE_CALIBRATION_NONE, + COVERAGE_CALIBRATION_BOX, + }; + + CoverageCalibration coverageCalibration; + + inline void applySizeScaleAndBias(float* outSize) const { + if (haveSizeScale) { + *outSize *= sizeScale; + } + if (haveSizeBias) { + *outSize += sizeBias; + } + if (*outSize < 0) { + *outSize = 0; + } + } + } mCalibration; + + // Affine location transformation/calibration + struct TouchAffineTransformation mAffineTransform; + + RawPointerAxes mRawPointerAxes; + + struct RawState { + nsecs_t when; + + // Raw pointer sample data. + RawPointerData rawPointerData; + + int32_t buttonState; + + // Scroll state. + int32_t rawVScroll; + int32_t rawHScroll; + + void copyFrom(const RawState& other) { + when = other.when; + rawPointerData.copyFrom(other.rawPointerData); + buttonState = other.buttonState; + rawVScroll = other.rawVScroll; + rawHScroll = other.rawHScroll; + } + + void clear() { + when = 0; + rawPointerData.clear(); + buttonState = 0; + rawVScroll = 0; + rawHScroll = 0; + } + }; + + struct CookedState { + // Cooked pointer sample data. + CookedPointerData cookedPointerData; + + // Id bits used to differentiate fingers, stylus and mouse tools. + BitSet32 fingerIdBits; + BitSet32 stylusIdBits; + BitSet32 mouseIdBits; + + int32_t buttonState; + + void copyFrom(const CookedState& other) { + cookedPointerData.copyFrom(other.cookedPointerData); + fingerIdBits = other.fingerIdBits; + stylusIdBits = other.stylusIdBits; + mouseIdBits = other.mouseIdBits; + buttonState = other.buttonState; + } + + void clear() { + cookedPointerData.clear(); + fingerIdBits.clear(); + stylusIdBits.clear(); + mouseIdBits.clear(); + buttonState = 0; + } + }; + + std::vector mRawStatesPending; + RawState mCurrentRawState; + CookedState mCurrentCookedState; + RawState mLastRawState; + CookedState mLastCookedState; + + // State provided by an external stylus + StylusState mExternalStylusState; + int64_t mExternalStylusId; + nsecs_t mExternalStylusFusionTimeout; + bool mExternalStylusDataPending; + + // True if we sent a HOVER_ENTER event. + bool mSentHoverEnter; + + // Have we assigned pointer IDs for this stream + bool mHavePointerIds; + + // Is the current stream of direct touch events aborted + bool mCurrentMotionAborted; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + // The pointer controller, or null if the device is not a pointer. + sp mPointerController; + + std::vector mVirtualKeys; + + virtual void configureParameters(); + virtual void dumpParameters(std::string& dump); + virtual void configureRawPointerAxes(); + virtual void dumpRawPointerAxes(std::string& dump); + virtual void configureSurface(nsecs_t when, bool* outResetNeeded); + virtual void dumpSurface(std::string& dump); + virtual void configureVirtualKeys(); + virtual void dumpVirtualKeys(std::string& dump); + virtual void parseCalibration(); + virtual void resolveCalibration(); + virtual void dumpCalibration(std::string& dump); + virtual void updateAffineTransformation(); + virtual void dumpAffineTransformation(std::string& dump); + virtual void resolveExternalStylusPresence(); + virtual bool hasStylus() const = 0; + virtual bool hasExternalStylus() const; + + virtual void syncTouch(nsecs_t when, RawState* outState) = 0; + +private: + // The current viewport. + // The components of the viewport are specified in the display's rotated orientation. + DisplayViewport mViewport; + + // The surface orientation, width and height set by configureSurface(). + // The width and height are derived from the viewport but are specified + // in the natural orientation. + // The surface origin specifies how the surface coordinates should be translated + // to align with the logical display coordinate space. + int32_t mSurfaceWidth; + int32_t mSurfaceHeight; + int32_t mSurfaceLeft; + int32_t mSurfaceTop; + + // Similar to the surface coordinates, but in the raw display coordinate space rather than in + // the logical coordinate space. + int32_t mPhysicalWidth; + int32_t mPhysicalHeight; + int32_t mPhysicalLeft; + int32_t mPhysicalTop; + + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. + int32_t mSurfaceOrientation; + + // Translation and scaling factors, orientation-independent. + float mXTranslate; + float mXScale; + float mXPrecision; + + float mYTranslate; + float mYScale; + float mYPrecision; + + float mGeometricScale; + + float mPressureScale; + + float mSizeScale; + + float mOrientationScale; + + float mDistanceScale; + + bool mHaveTilt; + float mTiltXCenter; + float mTiltXScale; + float mTiltYCenter; + float mTiltYScale; + + bool mExternalStylusConnected; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + + bool haveSize; + InputDeviceInfo::MotionRange size; + + bool haveTouchSize; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + + bool haveToolSize; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + + bool haveOrientation; + InputDeviceInfo::MotionRange orientation; + + bool haveDistance; + InputDeviceInfo::MotionRange distance; + + bool haveTilt; + InputDeviceInfo::MotionRange tilt; + + OrientedRanges() { clear(); } + + void clear() { + haveSize = false; + haveTouchSize = false; + haveToolSize = false; + haveOrientation = false; + haveDistance = false; + haveTilt = false; + } + } mOrientedRanges; + + // Oriented dimensions and precision. + float mOrientedXPrecision; + float mOrientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + bool ignored; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } mCurrentVirtualKey; + + // Scale factor for gesture or mouse based pointer movements. + float mPointerXMovementScale; + float mPointerYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float mPointerXZoomScale; + float mPointerYZoomScale; + + // The maximum swipe width. + float mPointerGestureMaxSwipeWidth; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + enum PointerUsage { + POINTER_USAGE_NONE, + POINTER_USAGE_GESTURES, + POINTER_USAGE_STYLUS, + POINTER_USAGE_MOUSE, + }; + PointerUsage mPointerUsage; + + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Exactly one finger dragging following a tap. + // Pointer follows the active finger. + // Emits DOWN, MOVE and UP events at the pointer location. + // + // Detect double-taps when the finger goes up while in TAP_DRAG mode. + TAP_DRAG, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + BUTTON_CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + // + // Detect taps when the finger goes up while in HOVER mode. + HOVER, + + // Exactly two fingers but neither have moved enough to clearly indicate + // whether a swipe or freeform gesture was intended. We consider the + // pointer to be pressed so this enables clicking or long-pressing on buttons. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. + PRESS, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // Time the first finger went down. + nsecs_t firstTouchTime; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties currentGestureProperties[MAX_POINTERS]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties lastGestureProperties[MAX_POINTERS]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time when the pointer went down for a TAP. + nsecs_t tapDownTime; + + // Time when the pointer went up for a TAP. + nsecs_t tapUpTime; + + // Location of initial tap. + float tapX, tapY; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // Reference points for multitouch gestures. + float referenceTouchX; // reference touch X/Y coordinates in surface units + float referenceTouchY; + float referenceGestureX; // reference gesture X/Y coordinates in pixels + float referenceGestureY; + + // Distance that each pointer has traveled which has not yet been + // subsumed into the reference gesture position. + BitSet32 referenceIdBits; + struct Delta { + float dx, dy; + }; + Delta referenceDeltas[MAX_POINTER_ID + 1]; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + firstTouchTime = LLONG_MIN; + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGestureIdBits.clear(); + downTime = 0; + velocityTracker.clear(); + resetTap(); + resetQuietTime(); + } + + void resetTap() { + tapDownTime = LLONG_MIN; + tapUpTime = LLONG_MIN; + } + + void resetQuietTime() { quietTime = LLONG_MIN; } + } mPointerGesture; + + struct PointerSimple { + PointerCoords currentCoords; + PointerProperties currentProperties; + PointerCoords lastCoords; + PointerProperties lastProperties; + + // True if the pointer is down. + bool down; + + // True if the pointer is hovering. + bool hovering; + + // Time the pointer last went down. + nsecs_t downTime; + + void reset() { + currentCoords.clear(); + currentProperties.clear(); + lastCoords.clear(); + lastProperties.clear(); + down = false; + hovering = false; + downTime = 0; + } + } mPointerSimple; + + // The pointer and scroll velocity controls. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + std::optional findViewport(); + + void resetExternalStylus(); + void clearStylusDataPendingFlags(); + + void sync(nsecs_t when); + + bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); + void processRawTouches(bool timeout); + void cookAndDispatch(nsecs_t when); + void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags); + + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); + void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); + void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); + void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); + const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); + void cookPointerData(); + void abortTouches(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); + void abortPointerUsage(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); + void abortPointerGestures(nsecs_t when, uint32_t policyFlags); + bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout); + + void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); + void abortPointerStylus(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); + void abortPointerMouse(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); + void abortPointerSimple(nsecs_t when, uint32_t policyFlags); + + bool assignExternalStylusId(const RawState& state, bool timeout); + void applyExternalStylusButtonState(nsecs_t when); + void applyExternalStylusTouchState(nsecs_t when); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords and properties for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, + const uint32_t* inIdToIndex, PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const; + + bool isPointInsideSurface(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); + + static void assignPointerIds(const RawState* last, RawState* current); + + const char* modeToString(DeviceMode deviceMode); +}; + +class SingleTouchInputMapper : public TouchInputMapper { +public: + explicit SingleTouchInputMapper(InputDevice* device); + virtual ~SingleTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; +}; + +class MultiTouchInputMapper : public TouchInputMapper { +public: + explicit MultiTouchInputMapper(InputDevice* device); + virtual ~MultiTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; + + // Specifies the pointer id bits that are in use, and their associated tracking id. + BitSet32 mPointerIdBits; + int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; +}; + +class ExternalStylusInputMapper : public InputMapper { +public: + explicit ExternalStylusInputMapper(InputDevice* device); + virtual ~ExternalStylusInputMapper() = default; + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + virtual void sync(nsecs_t when); + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; + RawAbsoluteAxisInfo mRawPressureAxis; + TouchButtonAccumulator mTouchButtonAccumulator; + + StylusState mStylusState; +}; + +class JoystickInputMapper : public InputMapper { +public: + explicit JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + struct Axis { + RawAbsoluteAxisInfo rawAxisInfo; + AxisInfo axisInfo; + + bool explicitlyMapped; // true if the axis was explicitly assigned an axis id + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + float highScale; // scale factor from raw to normalized values of high split + float highOffset; // offset to add after scaling for normalization of high split + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units/mm + + float filter; // filter out small variations of this size + float currentValue; // current value + float newValue; // most recent value + float highCurrentValue; // current value of high split + float highNewValue; // most recent value of high split + + void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, + bool explicitlyMapped, float scale, float offset, float highScale, + float highOffset, float min, float max, float flat, float fuzz, + float resolution) { + this->rawAxisInfo = rawAxisInfo; + this->axisInfo = axisInfo; + this->explicitlyMapped = explicitlyMapped; + this->scale = scale; + this->offset = offset; + this->highScale = highScale; + this->highOffset = highOffset; + this->min = min; + this->max = max; + this->flat = flat; + this->fuzz = fuzz; + this->resolution = resolution; + this->filter = 0; + resetValue(); + } + + void resetValue() { + this->currentValue = 0; + this->newValue = 0; + this->highCurrentValue = 0; + this->highNewValue = 0; + } + }; + + // Axes indexed by raw ABS_* axis index. + KeyedVector mAxes; + + void sync(nsecs_t when, bool force); + + bool haveAxis(int32_t axisId); + void pruneAxes(bool ignoreExplicitlyMappedAxes); + bool filterAxes(bool force); + + static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, + float min, float max); + static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, float thresholdValue); + + static bool isCenteredAxis(int32_t axis); + static int32_t getCompatAxis(int32_t axis); + + static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); + static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); +}; + +} // namespace android + +#endif // _UI_INPUT_READER_H diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h new file mode 100644 index 0000000000..9dfa951ff1 --- /dev/null +++ b/services/inputflinger/reader/include/TouchVideoDevice.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/** + * Represents a video device that uses v4l2 api to report touch heatmap data. + */ +class TouchVideoDevice { +public: + /** + * Create a new TouchVideoDevice for the path provided. + * Return nullptr upon failure. + */ + static std::unique_ptr create(std::string devicePath); + ~TouchVideoDevice(); + + bool hasValidFd() const { return mFd.get() != INVALID_FD; } + /** + * Obtain the file descriptor associated with this video device. + * Could be used for adding to epoll. + */ + int getFd() const { return mFd.get(); } + /** + * Get the name of this video device. + */ + const std::string& getName() const { return mName; } + /** + * Get the file path of this video device. + */ + const std::string& getPath() const { return mPath; } + /** + * Get the height of the heatmap frame + */ + uint32_t getHeight() const { return mHeight; } + /** + * Get the width of the heatmap frame + */ + uint32_t getWidth() const { return mWidth; } + /** + * Direct read of the frame. Stores the frame into internal buffer. + * Return the number of frames that were successfully read. + * + * This function should not be called unless buffer is ready! + * This must be checked with select, poll, epoll, or similar api first. + * If epoll indicates that there is data ready to read, but this function + * returns zero, then it is likely an error occurred. + */ + size_t readAndQueueFrames(); + /** + * Return all of the queued frames, and erase them from the local buffer. + * The returned frames are in the order that they were received from the + * v4l2 device, with the oldest frame at the index 0. + */ + std::vector consumeFrames(); + /** + * Get string representation of this video device. + */ + std::string dump() const; + +private: + android::base::unique_fd mFd; + std::string mName; + std::string mPath; + + uint32_t mHeight; + uint32_t mWidth; + + static constexpr int INVALID_FD = -1; + /** + * How many buffers to request for heatmap. + * The kernel driver will be allocating these buffers for us, + * and will provide memory locations to read these from. + */ + static constexpr size_t NUM_BUFFERS = 3; + std::array mReadLocations; + /** + * How many buffers to keep for the internal queue. When the internal buffer + * exceeds this capacity, oldest frames will be dropped. + */ + static constexpr size_t MAX_QUEUE_SIZE = 10; + std::vector mFrames; + + /** + * The constructor is private because opening a v4l2 device requires many checks. + * To get a new TouchVideoDevice, use 'create' instead. + */ + explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, uint32_t height, + uint32_t width, + const std::array& readLocations); + /** + * Read all currently available frames. + */ + std::vector readFrames(); + /** + * Read a single frame. May return nullopt if no data is currently available for reading. + */ + std::optional readFrame(); +}; +} // namespace android +#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 9054316204..c4f8626a98 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -32,4 +32,7 @@ cc_test { "libinputflinger_base", "libinputservice", ], + header_libs: [ + "libinputreader_headers", + ], } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 09df2a70c7..55c0497c4d 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "../InputReader.h" +#include "InputReader.h" #include "TestInputListener.h" #include -- cgit v1.2.3-59-g8ed1b From baa5c82401ca50ef496818b5949ca06812f5f8ef Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 30 Aug 2019 15:27:05 -0700 Subject: Split InputReader into multiple files This CL: 1) Moves commonly used macros and shared definitions into Macros.h, which is included as the first #include statement in the files that require it. 2) Creates InputReaderContext.h and moves the context definition outside the InputReader.h file. 3) Moves InputDevice, InputMapper, StylusState, and all of the individual InputMappers into their own header and cpp file combinations. 4) Moves all Accumulator classes that are shared between multiple InputMappers into their own header and cpp file combinations. The definitions of Accumulators that are unique to a mapper are kept in the header file of the mapper. 5) Moves constants and static methods that were shared between the TouchInputMapper and CursorInputMapper to TouchCursorInputMapperCommon.h. 6) Creates an 'include' directory, as well as a header library build rule, to hold all the header files required by InputReader_test.cpp. 7) Runs clang-format on the newly create files to fix formatting inconsistencies. Bug: 140139676 Test: atest inputflinger_tests Test: Touch, keyboard, trackpad, and mouse works on crosshatch Change-Id: Ib1ac871c7f4199729bf6d0e6a53ed907af318986 --- services/inputflinger/reader/Android.bp | 16 + .../reader/CursorButtonAccumulator.cpp | 101 + services/inputflinger/reader/CursorInputMapper.cpp | 479 ++ .../reader/CursorScrollAccumulator.cpp | 59 + .../reader/ExternalStylusInputMapper.cpp | 92 + .../reader/ExternalStylusInputMapper.h | 51 + services/inputflinger/reader/InputDevice.cpp | 380 ++ services/inputflinger/reader/InputMapper.cpp | 101 + services/inputflinger/reader/InputReader.cpp | 6712 +------------------- .../inputflinger/reader/InputReaderFactory.cpp | 1 + .../inputflinger/reader/JoystickInputMapper.cpp | 409 ++ services/inputflinger/reader/JoystickInputMapper.h | 111 + .../inputflinger/reader/KeyboardInputMapper.cpp | 433 ++ services/inputflinger/reader/Macros.h | 84 + .../inputflinger/reader/MultiTouchInputMapper.cpp | 357 ++ .../reader/RotaryEncoderInputMapper.cpp | 134 + .../inputflinger/reader/RotaryEncoderInputMapper.h | 49 + .../inputflinger/reader/SingleTouchInputMapper.cpp | 85 + .../reader/SingleTouchMotionAccumulator.cpp | 76 + services/inputflinger/reader/SwitchInputMapper.cpp | 76 + .../inputflinger/reader/TouchButtonAccumulator.cpp | 162 + .../reader/TouchCursorInputMapperCommon.h | 88 + services/inputflinger/reader/TouchInputMapper.cpp | 3883 +++++++++++ .../inputflinger/reader/VibratorInputMapper.cpp | 131 + services/inputflinger/reader/VibratorInputMapper.h | 53 + .../reader/include/CursorButtonAccumulator.h | 52 + .../reader/include/CursorInputMapper.h | 123 + .../reader/include/CursorScrollAccumulator.h | 60 + services/inputflinger/reader/include/EventHub.h | 1 - services/inputflinger/reader/include/InputDevice.h | 145 + services/inputflinger/reader/include/InputMapper.h | 92 + services/inputflinger/reader/include/InputReader.h | 1536 +---- .../reader/include/InputReaderContext.h | 65 + .../reader/include/KeyboardInputMapper.h | 102 + .../reader/include/MultiTouchInputMapper.h | 115 + .../reader/include/SingleTouchInputMapper.h | 44 + .../reader/include/SingleTouchMotionAccumulator.h | 57 + services/inputflinger/reader/include/StylusState.h | 53 + .../reader/include/SwitchInputMapper.h | 45 + .../reader/include/TouchButtonAccumulator.h | 66 + .../inputflinger/reader/include/TouchInputMapper.h | 764 +++ .../inputflinger/reader/include/TouchVideoDevice.h | 8 +- services/inputflinger/tests/InputReader_test.cpp | 8 + 43 files changed, 9224 insertions(+), 8235 deletions(-) create mode 100644 services/inputflinger/reader/CursorButtonAccumulator.cpp create mode 100644 services/inputflinger/reader/CursorInputMapper.cpp create mode 100644 services/inputflinger/reader/CursorScrollAccumulator.cpp create mode 100644 services/inputflinger/reader/ExternalStylusInputMapper.cpp create mode 100644 services/inputflinger/reader/ExternalStylusInputMapper.h create mode 100644 services/inputflinger/reader/InputDevice.cpp create mode 100644 services/inputflinger/reader/InputMapper.cpp create mode 100644 services/inputflinger/reader/JoystickInputMapper.cpp create mode 100644 services/inputflinger/reader/JoystickInputMapper.h create mode 100644 services/inputflinger/reader/KeyboardInputMapper.cpp create mode 100644 services/inputflinger/reader/Macros.h create mode 100644 services/inputflinger/reader/MultiTouchInputMapper.cpp create mode 100644 services/inputflinger/reader/RotaryEncoderInputMapper.cpp create mode 100644 services/inputflinger/reader/RotaryEncoderInputMapper.h create mode 100644 services/inputflinger/reader/SingleTouchInputMapper.cpp create mode 100644 services/inputflinger/reader/SingleTouchMotionAccumulator.cpp create mode 100644 services/inputflinger/reader/SwitchInputMapper.cpp create mode 100644 services/inputflinger/reader/TouchButtonAccumulator.cpp create mode 100644 services/inputflinger/reader/TouchCursorInputMapperCommon.h create mode 100644 services/inputflinger/reader/TouchInputMapper.cpp create mode 100644 services/inputflinger/reader/VibratorInputMapper.cpp create mode 100644 services/inputflinger/reader/VibratorInputMapper.h create mode 100644 services/inputflinger/reader/include/CursorButtonAccumulator.h create mode 100644 services/inputflinger/reader/include/CursorInputMapper.h create mode 100644 services/inputflinger/reader/include/CursorScrollAccumulator.h create mode 100644 services/inputflinger/reader/include/InputDevice.h create mode 100644 services/inputflinger/reader/include/InputMapper.h create mode 100644 services/inputflinger/reader/include/InputReaderContext.h create mode 100644 services/inputflinger/reader/include/KeyboardInputMapper.h create mode 100644 services/inputflinger/reader/include/MultiTouchInputMapper.h create mode 100644 services/inputflinger/reader/include/SingleTouchInputMapper.h create mode 100644 services/inputflinger/reader/include/SingleTouchMotionAccumulator.h create mode 100644 services/inputflinger/reader/include/StylusState.h create mode 100644 services/inputflinger/reader/include/SwitchInputMapper.h create mode 100644 services/inputflinger/reader/include/TouchButtonAccumulator.h create mode 100644 services/inputflinger/reader/include/TouchInputMapper.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index c3e8ceda56..7b307651d5 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -22,10 +22,26 @@ cc_library_shared { defaults: ["inputflinger_defaults"], srcs: [ + "CursorButtonAccumulator.cpp", + "CursorInputMapper.cpp", + "CursorScrollAccumulator.cpp", "EventHub.cpp", + "ExternalStylusInputMapper.cpp", + "InputDevice.cpp", + "InputMapper.cpp", "InputReader.cpp", "InputReaderFactory.cpp", + "JoystickInputMapper.cpp", + "KeyboardInputMapper.cpp", + "MultiTouchInputMapper.cpp", + "RotaryEncoderInputMapper.cpp", + "SingleTouchInputMapper.cpp", + "SingleTouchMotionAccumulator.cpp", + "SwitchInputMapper.cpp", + "TouchButtonAccumulator.cpp", + "TouchInputMapper.cpp", "TouchVideoDevice.cpp", + "VibratorInputMapper.cpp", ], shared_libs: [ diff --git a/services/inputflinger/reader/CursorButtonAccumulator.cpp b/services/inputflinger/reader/CursorButtonAccumulator.cpp new file mode 100644 index 0000000000..0337d51126 --- /dev/null +++ b/services/inputflinger/reader/CursorButtonAccumulator.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorButtonAccumulator::CursorButtonAccumulator() { + clearButtons(); +} + +void CursorButtonAccumulator::reset(InputDevice* device) { + mBtnLeft = device->isKeyPressed(BTN_LEFT); + mBtnRight = device->isKeyPressed(BTN_RIGHT); + mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); + mBtnBack = device->isKeyPressed(BTN_BACK); + mBtnSide = device->isKeyPressed(BTN_SIDE); + mBtnForward = device->isKeyPressed(BTN_FORWARD); + mBtnExtra = device->isKeyPressed(BTN_EXTRA); + mBtnTask = device->isKeyPressed(BTN_TASK); +} + +void CursorButtonAccumulator::clearButtons() { + mBtnLeft = 0; + mBtnRight = 0; + mBtnMiddle = 0; + mBtnBack = 0; + mBtnSide = 0; + mBtnForward = 0; + mBtnExtra = 0; + mBtnTask = 0; +} + +void CursorButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_LEFT: + mBtnLeft = rawEvent->value; + break; + case BTN_RIGHT: + mBtnRight = rawEvent->value; + break; + case BTN_MIDDLE: + mBtnMiddle = rawEvent->value; + break; + case BTN_BACK: + mBtnBack = rawEvent->value; + break; + case BTN_SIDE: + mBtnSide = rawEvent->value; + break; + case BTN_FORWARD: + mBtnForward = rawEvent->value; + break; + case BTN_EXTRA: + mBtnExtra = rawEvent->value; + break; + case BTN_TASK: + mBtnTask = rawEvent->value; + break; + } + } +} + +uint32_t CursorButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnLeft) { + result |= AMOTION_EVENT_BUTTON_PRIMARY; + } + if (mBtnRight) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnMiddle) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + if (mBtnBack || mBtnSide) { + result |= AMOTION_EVENT_BUTTON_BACK; + } + if (mBtnForward || mBtnExtra) { + result |= AMOTION_EVENT_BUTTON_FORWARD; + } + return result; +} + +} // namespace android diff --git a/services/inputflinger/reader/CursorInputMapper.cpp b/services/inputflinger/reader/CursorInputMapper.cpp new file mode 100644 index 0000000000..f69138ea09 --- /dev/null +++ b/services/inputflinger/reader/CursorInputMapper.cpp @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "CursorInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +namespace android { + +// --- CursorMotionAccumulator --- + +CursorMotionAccumulator::CursorMotionAccumulator() { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::clearRelativeAxes() { + mRelX = 0; + mRelY = 0; +} + +void CursorMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_X: + mRelX = rawEvent->value; + break; + case REL_Y: + mRelY = rawEvent->value; + break; + } + } +} + +void CursorMotionAccumulator::finishSync() { + clearRelativeAxes(); +} + +// --- CursorInputMapper --- + +CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} + +CursorInputMapper::~CursorInputMapper() {} + +uint32_t CursorInputMapper::getSources() { + return mSource; +} + +void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mParameters.mode == Parameters::MODE_POINTER) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); + } + } else { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } +} + +void CursorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Cursor Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeVWheel())); + dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); + dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); + dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); + dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + mCursorScrollAccumulator.configure(getDevice()); + + // Configure basic parameters. + configureParameters(); + + // Configure device mode. + switch (mParameters.mode) { + case Parameters::MODE_POINTER_RELATIVE: + // Should not happen during first time configuration. + ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); + mParameters.mode = Parameters::MODE_POINTER; + [[fallthrough]]; + case Parameters::MODE_POINTER: + mSource = AINPUT_SOURCE_MOUSE; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + mXScale = 1.0f; + mYScale = 1.0f; + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + break; + case Parameters::MODE_NAVIGATION: + mSource = AINPUT_SOURCE_TRACKBALL; + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + break; + } + + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; + } + + if ((!changes && config->pointerCapture) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { + if (config->pointerCapture) { + if (mParameters.mode == Parameters::MODE_POINTER) { + mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + mSource = AINPUT_SOURCE_MOUSE_RELATIVE; + // Keep PointerController around in order to preserve the pointer position. + mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else { + ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); + } + } else { + if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { + mParameters.mode = Parameters::MODE_POINTER; + mSource = AINPUT_SOURCE_MOUSE; + } else { + ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); + } + } + bumpGeneration(); + if (changes) { + getDevice()->notifyReset(when); + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mOrientation = DISPLAY_ORIENTATION_0; + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } + } + + // Update the PointerController if viewports changed. + if (mParameters.mode == Parameters::MODE_POINTER) { + getPolicy()->obtainPointerController(getDeviceId()); + } + bumpGeneration(); + } +} + +void CursorInputMapper::configureParameters() { + mParameters.mode = Parameters::MODE_POINTER; + String8 cursorModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { + if (cursorModeString == "navigation") { + mParameters.mode = Parameters::MODE_NAVIGATION; + } else if (cursorModeString != "pointer" && cursorModeString != "default") { + ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); + } + } + + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + mParameters.hasAssociatedDisplay = true; + } +} + +void CursorInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); + + switch (mParameters.mode) { + case Parameters::MODE_POINTER: + dump += INDENT4 "Mode: pointer\n"; + break; + case Parameters::MODE_POINTER_RELATIVE: + dump += INDENT4 "Mode: relative pointer\n"; + break; + case Parameters::MODE_NAVIGATION: + dump += INDENT4 "Mode: navigation\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void CursorInputMapper::reset(nsecs_t when) { + mButtonState = 0; + mDownTime = 0; + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mCursorButtonAccumulator.reset(getDevice()); + mCursorMotionAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void CursorInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorMotionAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void CursorInputMapper::sync(nsecs_t when) { + int32_t lastButtonState = mButtonState; + int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); + mButtonState = currentButtonState; + + bool wasDown = isPointerDown(lastButtonState); + bool down = isPointerDown(currentButtonState); + bool downChanged; + if (!wasDown && down) { + mDownTime = when; + downChanged = true; + } else if (wasDown && !down) { + downChanged = true; + } else { + downChanged = false; + } + nsecs_t downTime = mDownTime; + bool buttonsChanged = currentButtonState != lastButtonState; + int32_t buttonsPressed = currentButtonState & ~lastButtonState; + int32_t buttonsReleased = lastButtonState & ~currentButtonState; + + float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; + float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; + bool moved = deltaX != 0 || deltaY != 0; + + // Rotate delta according to orientation if needed. + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && + (deltaX != 0.0f || deltaY != 0.0f)) { + rotateDelta(mOrientation, &deltaX, &deltaY); + } + + // Move the pointer. + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); + float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); + bool scrolled = vscroll != 0 || hscroll != 0; + + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mSource == AINPUT_SOURCE_MOUSE) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + + if (buttonsChanged) { + mPointerController->setButtonState(currentButtonState); + } + + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); + displayId = mPointerController->getDisplayId(); + } else { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); + displayId = ADISPLAY_ID_NONE; + } + + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); + + // Moving an external trackball or mouse should wake the device. + // We don't do this for internal cursor devices to prevent them from waking up + // the device in your pocket. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + // Synthesize key down from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + // Send motion event. + if (downChanged || moved || scrolled || buttonsChanged) { + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = lastButtonState; + int32_t motionEventAction; + if (downChanged) { + motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } else { + motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; + } + + if (buttonsReleased) { + BitSet32 released(buttonsReleased); + while (!released.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); + buttonState &= ~actionButton; + NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&releaseArgs); + } + } + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + + if (buttonsPressed) { + BitSet32 pressed(buttonsPressed); + while (!pressed.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); + buttonState |= actionButton; + NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&pressArgs); + } + } + + ALOG_ASSERT(buttonState == currentButtonState); + + // Send hover move after UP to tell the application that the mouse is hovering now. + if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&hoverArgs); + } + + // Send scroll events. + if (scrolled) { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + } + + // Synthesize key up from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + mCursorMotionAccumulator.finishSync(); + mCursorScrollAccumulator.finishSync(); +} + +int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + +void CursorInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +std::optional CursorInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mParameters.mode == Parameters::MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + // If the device is orientationAware and not a mouse, + // it expects to dispatch events to any display + return std::make_optional(ADISPLAY_ID_NONE); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/CursorScrollAccumulator.cpp b/services/inputflinger/reader/CursorScrollAccumulator.cpp new file mode 100644 index 0000000000..d744096d94 --- /dev/null +++ b/services/inputflinger/reader/CursorScrollAccumulator.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorScrollAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::configure(InputDevice* device) { + mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); + mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +} + +void CursorScrollAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::clearRelativeAxes() { + mRelWheel = 0; + mRelHWheel = 0; +} + +void CursorScrollAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_WHEEL: + mRelWheel = rawEvent->value; + break; + case REL_HWHEEL: + mRelHWheel = rawEvent->value; + break; + } + } +} + +void CursorScrollAccumulator::finishSync() { + clearRelativeAxes(); +} + +} // namespace android diff --git a/services/inputflinger/reader/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/ExternalStylusInputMapper.cpp new file mode 100644 index 0000000000..9aa0770245 --- /dev/null +++ b/services/inputflinger/reader/ExternalStylusInputMapper.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "ExternalStylusInputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} + +uint32_t ExternalStylusInputMapper::getSources() { + return AINPUT_SOURCE_STYLUS; +} + +void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); +} + +void ExternalStylusInputMapper::dump(std::string& dump) { + dump += INDENT2 "External Stylus Input Mapper:\n"; + dump += INDENT3 "Raw Stylus Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); + dump += INDENT3 "Stylus State:\n"; + dumpStylusState(dump, mStylusState); +} + +void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); + mTouchButtonAccumulator.configure(getDevice()); +} + +void ExternalStylusInputMapper::reset(nsecs_t when) { + InputDevice* device = getDevice(); + mSingleTouchMotionAccumulator.reset(device); + mTouchButtonAccumulator.reset(device); + InputMapper::reset(when); +} + +void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { + mSingleTouchMotionAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void ExternalStylusInputMapper::sync(nsecs_t when) { + mStylusState.clear(); + + mStylusState.when = when; + + mStylusState.toolType = mTouchButtonAccumulator.getToolType(); + if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + + int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + if (mRawPressureAxis.valid) { + mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; + } else if (mTouchButtonAccumulator.isToolActive()) { + mStylusState.pressure = 1.0f; + } else { + mStylusState.pressure = 0.0f; + } + + mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); + + mContext->dispatchExternalStylusState(mStylusState); +} + +} // namespace android diff --git a/services/inputflinger/reader/ExternalStylusInputMapper.h b/services/inputflinger/reader/ExternalStylusInputMapper.h new file mode 100644 index 0000000000..9764fbb3c1 --- /dev/null +++ b/services/inputflinger/reader/ExternalStylusInputMapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H +#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H + +#include "InputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "StylusState.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +class ExternalStylusInputMapper : public InputMapper { +public: + explicit ExternalStylusInputMapper(InputDevice* device); + virtual ~ExternalStylusInputMapper() = default; + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + virtual void sync(nsecs_t when); + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; + RawAbsoluteAxisInfo mRawPressureAxis; + TouchButtonAccumulator mTouchButtonAccumulator; + + StylusState mStylusState; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp new file mode 100644 index 0000000000..7fed61f09d --- /dev/null +++ b/services/inputflinger/reader/InputDevice.cpp @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputDevice.h" + +#include "InputMapper.h" + +namespace android { + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes) + : mContext(context), + mId(id), + mGeneration(generation), + mControllerNumber(controllerNumber), + mIdentifier(identifier), + mClasses(classes), + mSources(0), + mIsExternal(false), + mHasMic(false), + mDropUntilNextSync(false) {} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; + } + mMappers.clear(); +} + +bool InputDevice::isEnabled() { + return getEventHub()->isDeviceEnabled(mId); +} + +void InputDevice::setEnabled(bool enabled, nsecs_t when) { + if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { + ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " + "but the corresponding viewport is not found", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + + if (isEnabled() == enabled) { + return; + } + + if (enabled) { + getEventHub()->enableDevice(mId); + reset(when); + } else { + reset(when); + getEventHub()->disableDevice(mId); + } + // Must change generation to flag this device as changed + bumpGeneration(); +} + +void InputDevice::dump(std::string& dump) { + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + + dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), + deviceInfo.getDisplayName().c_str()); + dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); + dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); + dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); + if (mAssociatedDisplayPort) { + dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); + } else { + dump += "\n"; + } + dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); + dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); + dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); + + const std::vector& ranges = deviceInfo.getMotionRanges(); + if (!ranges.empty()) { + dump += INDENT2 "Motion Ranges:\n"; + for (size_t i = 0; i < ranges.size(); i++) { + const InputDeviceInfo::MotionRange& range = ranges[i]; + const char* label = getAxisLabel(range.axis); + char name[32]; + if (label) { + strncpy(name, label, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else { + snprintf(name, sizeof(name), "%d", range.axis); + } + dump += StringPrintf(INDENT3 + "%s: source=0x%08x, " + "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", + name, range.source, range.min, range.max, range.flat, range.fuzz, + range.resolution); + } + } + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->dump(dump); + } +} + +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.push_back(mapper); +} + +void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + mSources = 0; + + if (!isIgnored()) { + if (!changes) { // first time only + mContext->getEventHub()->getConfiguration(mId, &mConfiguration); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + sp keyboardLayout = + mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); + if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); + if (mAlias != alias) { + mAlias = alias; + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { + auto it = config->disabledDevices.find(mId); + bool enabled = it == config->disabledDevices.end(); + setEnabled(enabled, when); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + // In most situations, no port will be specified. + mAssociatedDisplayPort = std::nullopt; + mAssociatedViewport = std::nullopt; + // Find the display port that corresponds to the current input port. + const std::string& inputPort = mIdentifier.location; + if (!inputPort.empty()) { + const std::unordered_map& ports = config->portAssociations; + const auto& displayPort = ports.find(inputPort); + if (displayPort != ports.end()) { + mAssociatedDisplayPort = std::make_optional(displayPort->second); + } + } + + // If the device was explicitly disabled by the user, it would be present in the + // "disabledDevices" list. If it is associated with a specific display, and it was not + // explicitly disabled, then enable/disable the device based on whether we can find the + // corresponding viewport. + bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); + if (mAssociatedDisplayPort) { + mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); + if (!mAssociatedViewport) { + ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " + "but the corresponding viewport is not found.", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + } + + if (changes) { + // For first-time configuration, only allow device to be disabled after mappers have + // finished configuring. This is because we need to read some of the properties from + // the device's open fd. + setEnabled(enabled, when); + } + } + + for (InputMapper* mapper : mMappers) { + mapper->configure(when, config, changes); + mSources |= mapper->getSources(); + } + + // If a device is just plugged but it might be disabled, we need to update some info like + // axis range of touch from each InputMapper first, then disable it. + if (!changes) { + setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); + } + } +} + +void InputDevice::reset(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->reset(when); + } + + mContext->updateGlobalMetaState(); + + notifyReset(when); +} + +void InputDevice::process(const RawEvent* rawEvents, size_t count) { + // Process all of the events in order for each mapper. + // We cannot simply ask each mapper to process them in bulk because mappers may + // have side-effects that must be interleaved. For example, joystick movement events and + // gamepad button presses are handled by different mappers but they should be dispatched + // in the order received. + for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { +#if DEBUG_RAW_EVENTS + ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, + rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when); +#endif + + if (mDropUntilNextSync) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + mDropUntilNextSync = false; +#if DEBUG_RAW_EVENTS + ALOGD("Recovered from input event buffer overrun."); +#endif + } else { +#if DEBUG_RAW_EVENTS + ALOGD("Dropped input event while waiting for next input sync."); +#endif + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { + ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); + mDropUntilNextSync = true; + reset(rawEvent->when); + } else { + for (InputMapper* mapper : mMappers) { + mapper->process(rawEvent); + } + } + --count; + } +} + +void InputDevice::timeoutExpired(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->timeoutExpired(when); + } +} + +void InputDevice::updateExternalStylusState(const StylusState& state) { + for (InputMapper* mapper : mMappers) { + mapper->updateExternalStylusState(state); + } +} + +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, + mHasMic); + for (InputMapper* mapper : mMappers) { + mapper->populateDeviceInfo(outDeviceInfo); + } +} + +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState); +} + +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, &InputMapper::getScanCodeState); +} + +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, &InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } + } + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + return result; +} + +void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputDevice::cancelVibrate(int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->cancelVibrate(token); + } +} + +void InputDevice::cancelTouch(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->cancelTouch(when); + } +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + for (InputMapper* mapper : mMappers) { + result |= mapper->getMetaState(); + } + return result; +} + +void InputDevice::updateMetaState(int32_t keyCode) { + for (InputMapper* mapper : mMappers) { + mapper->updateMetaState(keyCode); + } +} + +void InputDevice::fadePointer() { + for (InputMapper* mapper : mMappers) { + mapper->fadePointer(); + } +} + +void InputDevice::bumpGeneration() { + mGeneration = mContext->bumpGeneration(); +} + +void InputDevice::notifyReset(nsecs_t when) { + NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); + mContext->getListener()->notifyDeviceReset(&args); +} + +std::optional InputDevice::getAssociatedDisplayId() { + // Check if we had associated to the specific display. + if (mAssociatedViewport) { + return mAssociatedViewport->displayId; + } + + // No associated display port, check if some InputMapper is associated. + for (InputMapper* mapper : mMappers) { + std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); + if (associatedDisplayId) { + return associatedDisplayId; + } + } + + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/InputMapper.cpp b/services/inputflinger/reader/InputMapper.cpp new file mode 100644 index 0000000000..d941528d14 --- /dev/null +++ b/services/inputflinger/reader/InputMapper.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputMapper.h" + +#include "InputDevice.h" + +namespace android { + +InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} + +InputMapper::~InputMapper() {} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::dump(std::string& dump) {} + +void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) {} + +void InputMapper::reset(nsecs_t when) {} + +void InputMapper::timeoutExpired(nsecs_t when) {} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) {} + +void InputMapper::cancelVibrate(int32_t token) {} + +void InputMapper::cancelTouch(nsecs_t when) {} + +int32_t InputMapper::getMetaState() { + return 0; +} + +void InputMapper::updateMetaState(int32_t keyCode) {} + +void InputMapper::updateExternalStylusState(const StylusState& state) {} + +void InputMapper::fadePointer() {} + +status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { + return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); +} + +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + +void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name) { + if (axis.valid) { + dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, + axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); + } else { + dump += StringPrintf(INDENT4 "%s: unknown range\n", name); + } +} + +void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { + dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); + dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); + dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); +} + +} // namespace android diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 521f621dea..e57604cbe8 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -14,36 +14,21 @@ * limitations under the License. */ -#define LOG_TAG "InputReader" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -// Log debug messages about gesture detection. -#define DEBUG_GESTURES 0 - -// Log debug messages about the vibrator. -#define DEBUG_VIBRATOR 0 - -// Log debug messages about fusing stylus data. -#define DEBUG_STYLUS_FUSION 0 +#include "Macros.h" #include "InputReader.h" +#include "CursorInputMapper.h" +#include "ExternalStylusInputMapper.h" +#include "InputReaderContext.h" +#include "JoystickInputMapper.h" +#include "KeyboardInputMapper.h" +#include "MultiTouchInputMapper.h" +#include "RotaryEncoderInputMapper.h" +#include "SingleTouchInputMapper.h" +#include "SwitchInputMapper.h" +#include "VibratorInputMapper.h" + #include #include #include @@ -58,200 +43,11 @@ #include #include -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " -#define INDENT5 " " using android::base::StringPrintf; namespace android { -// --- Constants --- - -// Maximum number of slots supported when using the slot-based Multitouch Protocol B. -static constexpr size_t MAX_SLOTS = 32; - -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - -// Artificial latency on synthetic events created from stylus data without corresponding touch -// data. -static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); - -// --- Static Functions --- - -template -inline static T abs(const T& value) { - return value < 0 ? -value : value; -} - -template -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; -} - -inline static float avg(float x, float y) { - return (x + y) / 2; -} - -inline static float distance(float x1, float y1, float x2, float y2) { - return hypotf(x1 - x2, y1 - y2); -} - -inline static int32_t signExtendNybble(int32_t value) { - return value >= 8 ? value - 16 : value; -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { - if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; - } - } - } - return value; -} - -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, - {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, - {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, - {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, - {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, - {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, - {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, - {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, -}; -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], - size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; -} - -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, - {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, - {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, - {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, -}; -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, - keyCodeRotationMapSize); -} - -static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { - float temp; - switch (orientation) { - case DISPLAY_ORIENTATION_90: - temp = *deltaX; - *deltaX = *deltaY; - *deltaY = -temp; - break; - - case DISPLAY_ORIENTATION_180: - *deltaX = -*deltaX; - *deltaY = -*deltaY; - break; - - case DISPLAY_ORIENTATION_270: - temp = *deltaX; - *deltaX = -*deltaY; - *deltaY = temp; - break; - } -} - -static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { - return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0; -} - -// Returns true if the pointer should be reported as being down given the specified -// button states. This determines whether the event is reported as a touch event. -static bool isPointerDown(int32_t buttonState) { - return buttonState & - (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | - AMOTION_EVENT_BUTTON_TERTIARY); -} - -static float calculateCommonVector(float a, float b) { - if (a > 0 && b > 0) { - return a < b ? a : b; - } else if (a < 0 && b < 0) { - return a > b ? a : b; - } else { - return 0; - } -} - -static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, - int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { - if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && - (currentButtonState & buttonState)) || - (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && - !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); - } -} - -static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, - int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, - AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, - AKEYCODE_FORWARD); -} - -// --- InputReader --- - InputReader::InputReader(std::shared_ptr eventHub, const sp& policy, const sp& listener) @@ -974,6488 +770,4 @@ uint32_t InputReader::ContextImpl::getNextSequenceNum() { return (mReader->mNextSequenceNum)++; } -// --- InputDevice --- - -InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes) - : mContext(context), - mId(id), - mGeneration(generation), - mControllerNumber(controllerNumber), - mIdentifier(identifier), - mClasses(classes), - mSources(0), - mIsExternal(false), - mHasMic(false), - mDropUntilNextSync(false) {} - -InputDevice::~InputDevice() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - delete mMappers[i]; - } - mMappers.clear(); -} - -bool InputDevice::isEnabled() { - return getEventHub()->isDeviceEnabled(mId); -} - -void InputDevice::setEnabled(bool enabled, nsecs_t when) { - if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { - ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " - "but the corresponding viewport is not found", - getName().c_str(), *mAssociatedDisplayPort); - enabled = false; - } - - if (isEnabled() == enabled) { - return; - } - - if (enabled) { - getEventHub()->enableDevice(mId); - reset(when); - } else { - reset(when); - getEventHub()->disableDevice(mId); - } - // Must change generation to flag this device as changed - bumpGeneration(); -} - -void InputDevice::dump(std::string& dump) { - InputDeviceInfo deviceInfo; - getDeviceInfo(&deviceInfo); - - dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), - deviceInfo.getDisplayName().c_str()); - dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); - dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); - dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); - if (mAssociatedDisplayPort) { - dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); - } else { - dump += "\n"; - } - dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); - dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); - dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - - const std::vector& ranges = deviceInfo.getMotionRanges(); - if (!ranges.empty()) { - dump += INDENT2 "Motion Ranges:\n"; - for (size_t i = 0; i < ranges.size(); i++) { - const InputDeviceInfo::MotionRange& range = ranges[i]; - const char* label = getAxisLabel(range.axis); - char name[32]; - if (label) { - strncpy(name, label, sizeof(name)); - name[sizeof(name) - 1] = '\0'; - } else { - snprintf(name, sizeof(name), "%d", range.axis); - } - dump += StringPrintf(INDENT3 - "%s: source=0x%08x, " - "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", - name, range.source, range.min, range.max, range.flat, range.fuzz, - range.resolution); - } - } - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } -} - -void InputDevice::addMapper(InputMapper* mapper) { - mMappers.push_back(mapper); -} - -void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - mSources = 0; - - if (!isIgnored()) { - if (!changes) { // first time only - mContext->getEventHub()->getConfiguration(mId, &mConfiguration); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - sp keyboardLayout = - mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); - if (mAlias != alias) { - mAlias = alias; - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - auto it = config->disabledDevices.find(mId); - bool enabled = it == config->disabledDevices.end(); - setEnabled(enabled, when); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - // In most situations, no port will be specified. - mAssociatedDisplayPort = std::nullopt; - mAssociatedViewport = std::nullopt; - // Find the display port that corresponds to the current input port. - const std::string& inputPort = mIdentifier.location; - if (!inputPort.empty()) { - const std::unordered_map& ports = config->portAssociations; - const auto& displayPort = ports.find(inputPort); - if (displayPort != ports.end()) { - mAssociatedDisplayPort = std::make_optional(displayPort->second); - } - } - - // If the device was explicitly disabled by the user, it would be present in the - // "disabledDevices" list. If it is associated with a specific display, and it was not - // explicitly disabled, then enable/disable the device based on whether we can find the - // corresponding viewport. - bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); - if (mAssociatedDisplayPort) { - mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); - if (!mAssociatedViewport) { - ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " - "but the corresponding viewport is not found.", - getName().c_str(), *mAssociatedDisplayPort); - enabled = false; - } - } - - if (changes) { - // For first-time configuration, only allow device to be disabled after mappers have - // finished configuring. This is because we need to read some of the properties from - // the device's open fd. - setEnabled(enabled, when); - } - } - - for (InputMapper* mapper : mMappers) { - mapper->configure(when, config, changes); - mSources |= mapper->getSources(); - } - - // If a device is just plugged but it might be disabled, we need to update some info like - // axis range of touch from each InputMapper first, then disable it. - if (!changes) { - setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); - } - } -} - -void InputDevice::reset(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->reset(when); - } - - mContext->updateGlobalMetaState(); - - notifyReset(when); -} - -void InputDevice::process(const RawEvent* rawEvents, size_t count) { - // Process all of the events in order for each mapper. - // We cannot simply ask each mapper to process them in bulk because mappers may - // have side-effects that must be interleaved. For example, joystick movement events and - // gamepad button presses are handled by different mappers but they should be dispatched - // in the order received. - for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { -#if DEBUG_RAW_EVENTS - ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, - rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when); -#endif - - if (mDropUntilNextSync) { - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - mDropUntilNextSync = false; -#if DEBUG_RAW_EVENTS - ALOGD("Recovered from input event buffer overrun."); -#endif - } else { -#if DEBUG_RAW_EVENTS - ALOGD("Dropped input event while waiting for next input sync."); -#endif - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { - ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); - mDropUntilNextSync = true; - reset(rawEvent->when); - } else { - for (InputMapper* mapper : mMappers) { - mapper->process(rawEvent); - } - } - --count; - } -} - -void InputDevice::timeoutExpired(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->timeoutExpired(when); - } -} - -void InputDevice::updateExternalStylusState(const StylusState& state) { - for (InputMapper* mapper : mMappers) { - mapper->updateExternalStylusState(state); - } -} - -void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic); - for (InputMapper* mapper : mMappers) { - mapper->populateDeviceInfo(outDeviceInfo); - } -} - -int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState); -} - -int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getState(sourceMask, scanCode, &InputMapper::getScanCodeState); -} - -int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getState(sourceMask, switchCode, &InputMapper::getSwitchState); -} - -int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; - } - } - } - return result; -} - -bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } - } - return result; -} - -void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->vibrate(pattern, patternSize, repeat, token); - } -} - -void InputDevice::cancelVibrate(int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->cancelVibrate(token); - } -} - -void InputDevice::cancelTouch(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->cancelTouch(when); - } -} - -int32_t InputDevice::getMetaState() { - int32_t result = 0; - for (InputMapper* mapper : mMappers) { - result |= mapper->getMetaState(); - } - return result; -} - -void InputDevice::updateMetaState(int32_t keyCode) { - for (InputMapper* mapper : mMappers) { - mapper->updateMetaState(keyCode); - } -} - -void InputDevice::fadePointer() { - for (InputMapper* mapper : mMappers) { - mapper->fadePointer(); - } -} - -void InputDevice::bumpGeneration() { - mGeneration = mContext->bumpGeneration(); -} - -void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); -} - -std::optional InputDevice::getAssociatedDisplayId() { - // Check if we had associated to the specific display. - if (mAssociatedViewport) { - return mAssociatedViewport->displayId; - } - - // No associated display port, check if some InputMapper is associated. - for (InputMapper* mapper : mMappers) { - std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); - if (associatedDisplayId) { - return associatedDisplayId; - } - } - - return std::nullopt; -} - -// --- CursorButtonAccumulator --- - -CursorButtonAccumulator::CursorButtonAccumulator() { - clearButtons(); -} - -void CursorButtonAccumulator::reset(InputDevice* device) { - mBtnLeft = device->isKeyPressed(BTN_LEFT); - mBtnRight = device->isKeyPressed(BTN_RIGHT); - mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); - mBtnBack = device->isKeyPressed(BTN_BACK); - mBtnSide = device->isKeyPressed(BTN_SIDE); - mBtnForward = device->isKeyPressed(BTN_FORWARD); - mBtnExtra = device->isKeyPressed(BTN_EXTRA); - mBtnTask = device->isKeyPressed(BTN_TASK); -} - -void CursorButtonAccumulator::clearButtons() { - mBtnLeft = 0; - mBtnRight = 0; - mBtnMiddle = 0; - mBtnBack = 0; - mBtnSide = 0; - mBtnForward = 0; - mBtnExtra = 0; - mBtnTask = 0; -} - -void CursorButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_LEFT: - mBtnLeft = rawEvent->value; - break; - case BTN_RIGHT: - mBtnRight = rawEvent->value; - break; - case BTN_MIDDLE: - mBtnMiddle = rawEvent->value; - break; - case BTN_BACK: - mBtnBack = rawEvent->value; - break; - case BTN_SIDE: - mBtnSide = rawEvent->value; - break; - case BTN_FORWARD: - mBtnForward = rawEvent->value; - break; - case BTN_EXTRA: - mBtnExtra = rawEvent->value; - break; - case BTN_TASK: - mBtnTask = rawEvent->value; - break; - } - } -} - -uint32_t CursorButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnLeft) { - result |= AMOTION_EVENT_BUTTON_PRIMARY; - } - if (mBtnRight) { - result |= AMOTION_EVENT_BUTTON_SECONDARY; - } - if (mBtnMiddle) { - result |= AMOTION_EVENT_BUTTON_TERTIARY; - } - if (mBtnBack || mBtnSide) { - result |= AMOTION_EVENT_BUTTON_BACK; - } - if (mBtnForward || mBtnExtra) { - result |= AMOTION_EVENT_BUTTON_FORWARD; - } - return result; -} - -// --- CursorMotionAccumulator --- - -CursorMotionAccumulator::CursorMotionAccumulator() { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::clearRelativeAxes() { - mRelX = 0; - mRelY = 0; -} - -void CursorMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_X: - mRelX = rawEvent->value; - break; - case REL_Y: - mRelY = rawEvent->value; - break; - } - } -} - -void CursorMotionAccumulator::finishSync() { - clearRelativeAxes(); -} - -// --- CursorScrollAccumulator --- - -CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::configure(InputDevice* device) { - mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); - mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); -} - -void CursorScrollAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::clearRelativeAxes() { - mRelWheel = 0; - mRelHWheel = 0; -} - -void CursorScrollAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_WHEEL: - mRelWheel = rawEvent->value; - break; - case REL_HWHEEL: - mRelHWheel = rawEvent->value; - break; - } - } -} - -void CursorScrollAccumulator::finishSync() { - clearRelativeAxes(); -} - -// --- TouchButtonAccumulator --- - -TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); -} - -void TouchButtonAccumulator::configure(InputDevice* device) { - mHaveBtnTouch = device->hasKey(BTN_TOUCH); - mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || - device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || - device->hasKey(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDevice* device) { - mBtnTouch = device->isKeyPressed(BTN_TOUCH); - mBtnStylus = device->isKeyPressed(BTN_STYLUS); - // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); - mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; -} - -void TouchButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_TOUCH: - mBtnTouch = rawEvent->value; - break; - case BTN_STYLUS: - mBtnStylus = rawEvent->value; - break; - case BTN_STYLUS2: - case BTN_0: // BTN_0 is what gets mapped for the HID usage - // Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = rawEvent->value; - break; - case BTN_TOOL_FINGER: - mBtnToolFinger = rawEvent->value; - break; - case BTN_TOOL_PEN: - mBtnToolPen = rawEvent->value; - break; - case BTN_TOOL_RUBBER: - mBtnToolRubber = rawEvent->value; - break; - case BTN_TOOL_BRUSH: - mBtnToolBrush = rawEvent->value; - break; - case BTN_TOOL_PENCIL: - mBtnToolPencil = rawEvent->value; - break; - case BTN_TOOL_AIRBRUSH: - mBtnToolAirbrush = rawEvent->value; - break; - case BTN_TOOL_MOUSE: - mBtnToolMouse = rawEvent->value; - break; - case BTN_TOOL_LENS: - mBtnToolLens = rawEvent->value; - break; - case BTN_TOOL_DOUBLETAP: - mBtnToolDoubleTap = rawEvent->value; - break; - case BTN_TOOL_TRIPLETAP: - mBtnToolTripleTap = rawEvent->value; - break; - case BTN_TOOL_QUADTAP: - mBtnToolQuadTap = rawEvent->value; - break; - } - } -} - -uint32_t TouchButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnStylus) { - result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; - } - if (mBtnStylus2) { - result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; - } - return result; -} - -int32_t TouchButtonAccumulator::getToolType() const { - if (mBtnToolMouse || mBtnToolLens) { - return AMOTION_EVENT_TOOL_TYPE_MOUSE; - } - if (mBtnToolRubber) { - return AMOTION_EVENT_TOOL_TYPE_ERASER; - } - if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { - return AMOTION_EVENT_TOOL_TYPE_FINGER; - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -bool TouchButtonAccumulator::isToolActive() const { - return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || - mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || - mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; -} - -bool TouchButtonAccumulator::isHovering() const { - return mHaveBtnTouch && !mBtnTouch; -} - -bool TouchButtonAccumulator::hasStylus() const { - return mHaveStylus; -} - -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - -// --- RawPointerData --- - -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} - -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { - float x = 0, y = 0; - uint32_t count = touchingIdBits.count(); - if (count) { - for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const Pointer& pointer = pointerForId(id); - x += pointer.x; - y += pointer.y; - } - x /= count; - y /= count; - } - *outX = x; - *outY = y; -} - -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -// --- SingleTouchMotionAccumulator --- - -SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { - clearAbsoluteAxes(); -} - -void SingleTouchMotionAccumulator::reset(InputDevice* device) { - mAbsX = device->getAbsoluteAxisValue(ABS_X); - mAbsY = device->getAbsoluteAxisValue(ABS_Y); - mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); -} - -void SingleTouchMotionAccumulator::clearAbsoluteAxes() { - mAbsX = 0; - mAbsY = 0; - mAbsPressure = 0; - mAbsToolWidth = 0; - mAbsDistance = 0; - mAbsTiltX = 0; - mAbsTiltY = 0; -} - -void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - switch (rawEvent->code) { - case ABS_X: - mAbsX = rawEvent->value; - break; - case ABS_Y: - mAbsY = rawEvent->value; - break; - case ABS_PRESSURE: - mAbsPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAbsToolWidth = rawEvent->value; - break; - case ABS_DISTANCE: - mAbsDistance = rawEvent->value; - break; - case ABS_TILT_X: - mAbsTiltX = rawEvent->value; - break; - case ABS_TILT_Y: - mAbsTiltY = rawEvent->value; - break; - } - } -} - -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mSlots(nullptr), - mSlotCount(0), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { - delete[] mSlots; -} - -void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, - bool usingSlotsProtocol) { - mSlotCount = slotCount; - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - - delete[] mSlots; - mSlots = new Slot[slotCount]; -} - -void MultiTouchMotionAccumulator::reset(InputDevice* device) { - // Unfortunately there is no way to read the initial contents of the slots. - // So when we reset the accumulator, we must assume they are all zeroes. - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, - &initialSlot); - if (status) { - ALOGD("Could not retrieve current multitouch slot index. status=%d", status); - initialSlot = -1; - } - clearSlots(initialSlot); - } else { - clearSlots(-1); - } -} - -void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { - if (mSlots) { - for (size_t i = 0; i < mSlotCount; i++) { - mSlots[i].clear(); - } - } - mCurrentSlot = initialSlot; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); - } -#endif - } else { - Slot* slot = &mSlots[mCurrentSlot]; - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot->mInUse = true; - slot->mAbsMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - slot->mInUse = true; - slot->mAbsMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - slot->mInUse = true; - slot->mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->mInUse = true; - slot->mAbsMTTouchMinor = rawEvent->value; - slot->mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot->mInUse = true; - slot->mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->mInUse = true; - slot->mAbsMTWidthMinor = rawEvent->value; - slot->mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot->mInUse = true; - slot->mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot->mInUse = false; - } else { - slot->mInUse = true; - slot->mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->mInUse = true; - slot->mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot->mInUse = true; - slot->mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->mInUse = true; - slot->mAbsMTToolType = rawEvent->value; - slot->mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - clearSlots(-1); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - -// --- MultiTouchMotionAccumulator::Slot --- - -MultiTouchMotionAccumulator::Slot::Slot() { - clear(); -} - -void MultiTouchMotionAccumulator::Slot::clear() { - mInUse = false; - mHaveAbsMTTouchMinor = false; - mHaveAbsMTWidthMinor = false; - mHaveAbsMTToolType = false; - mAbsMTPositionX = 0; - mAbsMTPositionY = 0; - mAbsMTTouchMajor = 0; - mAbsMTTouchMinor = 0; - mAbsMTWidthMajor = 0; - mAbsMTWidthMinor = 0; - mAbsMTOrientation = 0; - mAbsMTTrackingId = -1; - mAbsMTPressure = 0; - mAbsMTDistance = 0; - mAbsMTToolType = 0; -} - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -// --- InputMapper --- - -InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} - -InputMapper::~InputMapper() {} - -void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { - info->addSource(getSources()); -} - -void InputMapper::dump(std::string& dump) {} - -void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) {} - -void InputMapper::reset(nsecs_t when) {} - -void InputMapper::timeoutExpired(nsecs_t when) {} - -int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return AKEY_STATE_UNKNOWN; -} - -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return false; -} - -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) {} - -void InputMapper::cancelVibrate(int32_t token) {} - -void InputMapper::cancelTouch(nsecs_t when) {} - -int32_t InputMapper::getMetaState() { - return 0; -} - -void InputMapper::updateMetaState(int32_t keyCode) {} - -void InputMapper::updateExternalStylusState(const StylusState& state) {} - -void InputMapper::fadePointer() {} - -status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); -} - -void InputMapper::bumpGeneration() { - mDevice->bumpGeneration(); -} - -void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, - const char* name) { - if (axis.valid) { - dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, - axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); - } else { - dump += StringPrintf(INDENT4 "%s: unknown range\n", name); - } -} - -void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { - dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); - dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); - dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); -} - -// --- SwitchInputMapper --- - -SwitchInputMapper::SwitchInputMapper(InputDevice* device) - : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} - -SwitchInputMapper::~SwitchInputMapper() {} - -uint32_t SwitchInputMapper::getSources() { - return AINPUT_SOURCE_SWITCH; -} - -void SwitchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_SW: - processSwitch(rawEvent->code, rawEvent->value); - break; - - case EV_SYN: - if (rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } - } -} - -void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { - if (switchCode >= 0 && switchCode < 32) { - if (switchValue) { - mSwitchValues |= 1 << switchCode; - } else { - mSwitchValues &= ~(1 << switchCode); - } - mUpdatedSwitchMask |= 1 << switchCode; - } -} - -void SwitchInputMapper::sync(nsecs_t when) { - if (mUpdatedSwitchMask) { - uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, - mUpdatedSwitchMask); - getListener()->notifySwitch(&args); - - mUpdatedSwitchMask = 0; - } -} - -int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); -} - -void SwitchInputMapper::dump(std::string& dump) { - dump += INDENT2 "Switch Input Mapper:\n"; - dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); -} - -// --- VibratorInputMapper --- - -VibratorInputMapper::VibratorInputMapper(InputDevice* device) - : InputMapper(device), mVibrating(false) {} - -VibratorInputMapper::~VibratorInputMapper() {} - -uint32_t VibratorInputMapper::getSources() { - return 0; -} - -void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setVibrator(true); -} - -void VibratorInputMapper::process(const RawEvent* rawEvent) { - // TODO: Handle FF_STATUS, although it does not seem to be widely supported. -} - -void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -#if DEBUG_VIBRATOR - std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } - ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), - patternStr.c_str(), repeat, token); -#endif - - mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; - mRepeat = repeat; - mToken = token; - mIndex = -1; - - nextStep(); -} - -void VibratorInputMapper::cancelVibrate(int32_t token) { -#if DEBUG_VIBRATOR - ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); -#endif - - if (mVibrating && mToken == token) { - stopVibrating(); - } -} - -void VibratorInputMapper::timeoutExpired(nsecs_t when) { - if (mVibrating) { - if (when >= mNextStepTime) { - nextStep(); - } else { - getContext()->requestTimeoutAtTime(mNextStepTime); - } - } -} - -void VibratorInputMapper::nextStep() { - mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { - if (mRepeat < 0) { - // We are done. - stopVibrating(); - return; - } - mIndex = mRepeat; - } - - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); -#endif - getEventHub()->vibrate(getDeviceId(), duration); - } else { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); - } - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; - getContext()->requestTimeoutAtTime(mNextStepTime); -#if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); -#endif -} - -void VibratorInputMapper::stopVibrating() { - mVibrating = false; -#if DEBUG_VIBRATOR - ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); -} - -void VibratorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Vibrator Input Mapper:\n"; - dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); -} - -// --- KeyboardInputMapper --- - -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) - : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} - -KeyboardInputMapper::~KeyboardInputMapper() {} - -uint32_t KeyboardInputMapper::getSources() { - return mSource; -} - -int32_t KeyboardInputMapper::getOrientation() { - if (mViewport) { - return mViewport->orientation; - } - return DISPLAY_ORIENTATION_0; -} - -int32_t KeyboardInputMapper::getDisplayId() { - if (mViewport) { - return mViewport->displayId; - } - return ADISPLAY_ID_NONE; -} - -void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); -} - -void KeyboardInputMapper::dump(std::string& dump) { - dump += INDENT2 "Keyboard Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); - dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); - dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -std::optional KeyboardInputMapper::findViewport( - nsecs_t when, const InputReaderConfiguration* config) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // No associated display defined, try to find default display if orientationAware. - if (mParameters.orientationAware) { - return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return std::nullopt; -} - -void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = findViewport(when, config); - } -} - -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { - int32_t mapped = 0; - if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } - } -} - -void KeyboardInputMapper::configureParameters() { - mParameters.orientationAware = false; - const PropertyMap& config = getDevice()->getConfiguration(); - config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } - - mParameters.handlesKeyRepeat = false; - config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); -} - -void KeyboardInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); - dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); -} - -void KeyboardInputMapper::reset(nsecs_t when) { - mMetaState = AMETA_NONE; - mDownTime = 0; - mKeyDowns.clear(); - mCurrentHidUsage = 0; - - resetLedState(); - - InputMapper::reset(when); -} - -void KeyboardInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); - } - break; - } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; - } - break; - } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } - } -} - -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE || scanCode >= KEY_OK || - (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || - (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { - int32_t keyCode; - int32_t keyMetaState; - uint32_t policyFlags; - - if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, - &keyMetaState, &policyFlags)) { - keyCode = AKEYCODE_UNKNOWN; - keyMetaState = mMetaState; - policyFlags = 0; - } - - if (down) { - // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware) { - keyCode = rotateKeyCode(keyCode, getOrientation()); - } - - // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) && - mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { - return; - } - if (policyFlags & POLICY_FLAG_GESTURE) { - mDevice->cancelTouch(when); - } - - KeyDown keyDown; - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; - mKeyDowns.push_back(keyDown); - } - - mDownTime = when; - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); - } else { - // key was not actually down - ALOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().c_str(), keyCode, scanCode); - return; - } - } - - if (updateMetaStateIfNeeded(keyCode, down)) { - // If global meta state changed send it along with the key. - // If it has not changed then we'll use what keymap gave us, - // since key replacement logic might temporarily reset a few - // meta bits for given key. - keyMetaState = mMetaState; - } - - nsecs_t downTime = mDownTime; - - // Key down on external an keyboard should wake the device. - // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. - // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mParameters.handlesKeyRepeat) { - policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; - } - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), - policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); -} - -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { - size_t n = mKeyDowns.size(); - for (size_t i = 0; i < n; i++) { - if (mKeyDowns[i].scanCode == scanCode) { - return i; - } - } - return -1; -} - -int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); -} - -int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); -} - -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); -} - -int32_t KeyboardInputMapper::getMetaState() { - return mMetaState; -} - -void KeyboardInputMapper::updateMetaState(int32_t keyCode) { - updateMetaStateIfNeeded(keyCode, false); -} - -bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { - int32_t oldMetaState = mMetaState; - int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); - bool metaStateChanged = oldMetaState != newMetaState; - if (metaStateChanged) { - mMetaState = newMetaState; - updateLedState(false); - - getContext()->updateGlobalMetaState(); - } - - return metaStateChanged; -} - -void KeyboardInputMapper::resetLedState() { - initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); - initializeLedState(mNumLockLedState, ALED_NUM_LOCK); - initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); - - updateLedState(true); -} - -void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { - ledState.avail = getEventHub()->hasLed(getDeviceId(), led); - ledState.on = false; -} - -void KeyboardInputMapper::updateLedState(bool reset) { - updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); - updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); - updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); -} - -void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, - int32_t modifier, bool reset) { - if (ledState.avail) { - bool desiredState = (mMetaState & modifier) != 0; - if (reset || ledState.on != desiredState) { - getEventHub()->setLedState(getDeviceId(), led, desiredState); - ledState.on = desiredState; - } - } -} - -std::optional KeyboardInputMapper::getAssociatedDisplayId() { - if (mViewport) { - return std::make_optional(mViewport->displayId); - } - return std::nullopt; -} - -// --- CursorInputMapper --- - -CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} - -CursorInputMapper::~CursorInputMapper() {} - -uint32_t CursorInputMapper::getSources() { - return mSource; -} - -void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mParameters.mode == Parameters::MODE_POINTER) { - float minX, minY, maxX, maxY; - if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); - } - } else { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); - } - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } -} - -void CursorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Cursor Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeVWheel())); - dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeHWheel())); - dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); - dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); - dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); - dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - mCursorScrollAccumulator.configure(getDevice()); - - // Configure basic parameters. - configureParameters(); - - // Configure device mode. - switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: - // Should not happen during first time configuration. - ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; - [[fallthrough]]; - case Parameters::MODE_POINTER: - mSource = AINPUT_SOURCE_MOUSE; - mXPrecision = 1.0f; - mYPrecision = 1.0f; - mXScale = 1.0f; - mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - break; - case Parameters::MODE_NAVIGATION: - mSource = AINPUT_SOURCE_TRACKBALL; - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - break; - } - - mVWheelScale = 1.0f; - mHWheelScale = 1.0f; - } - - if ((!changes && config->pointerCapture) || - (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { - if (config->pointerCapture) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; - mSource = AINPUT_SOURCE_MOUSE_RELATIVE; - // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else { - ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); - } - } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; - mSource = AINPUT_SOURCE_MOUSE; - } else { - ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); - } - } - bumpGeneration(); - if (changes) { - getDevice()->notifyReset(when); - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mOrientation = DISPLAY_ORIENTATION_0; - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } - } - - // Update the PointerController if viewports changed. - if (mParameters.mode == Parameters::MODE_POINTER) { - getPolicy()->obtainPointerController(getDeviceId()); - } - bumpGeneration(); - } -} - -void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; - String8 cursorModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { - if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; - } else if (cursorModeString != "pointer" && cursorModeString != "default") { - ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); - } - } - - mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { - mParameters.hasAssociatedDisplay = true; - } -} - -void CursorInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", - toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); -} - -void CursorInputMapper::reset(nsecs_t when) { - mButtonState = 0; - mDownTime = 0; - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mCursorButtonAccumulator.reset(getDevice()); - mCursorMotionAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void CursorInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorMotionAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void CursorInputMapper::sync(nsecs_t when) { - int32_t lastButtonState = mButtonState; - int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); - mButtonState = currentButtonState; - - bool wasDown = isPointerDown(lastButtonState); - bool down = isPointerDown(currentButtonState); - bool downChanged; - if (!wasDown && down) { - mDownTime = when; - downChanged = true; - } else if (wasDown && !down) { - downChanged = true; - } else { - downChanged = false; - } - nsecs_t downTime = mDownTime; - bool buttonsChanged = currentButtonState != lastButtonState; - int32_t buttonsPressed = currentButtonState & ~lastButtonState; - int32_t buttonsReleased = lastButtonState & ~currentButtonState; - - float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; - float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; - bool moved = deltaX != 0 || deltaY != 0; - - // Rotate delta according to orientation if needed. - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && - (deltaX != 0.0f || deltaY != 0.0f)) { - rotateDelta(mOrientation, &deltaX, &deltaY); - } - - // Move the pointer. - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); - float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); - bool scrolled = vscroll != 0 || hscroll != 0; - - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - int32_t displayId; - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); - } - - if (buttonsChanged) { - mPointerController->setButtonState(currentButtonState); - } - - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = mPointerController->getDisplayId(); - } else { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); - displayId = ADISPLAY_ID_NONE; - } - - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - - // Moving an external trackball or mouse should wake the device. - // We don't do this for internal cursor devices to prevent them from waking up - // the device in your pocket. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - // Synthesize key down from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - // Send motion event. - if (downChanged || moved || scrolled || buttonsChanged) { - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = lastButtonState; - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } else { - motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; - } - - if (buttonsReleased) { - BitSet32 released(buttonsReleased); - while (!released.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); - buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); - } - } - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - - if (buttonsPressed) { - BitSet32 pressed(buttonsPressed); - while (!pressed.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); - buttonState |= actionButton; - NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); - } - } - - ALOG_ASSERT(buttonState == currentButtonState); - - // Send hover move after UP to tell the application that the mouse is hovering now. - if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); - } - - // Send scroll events. - if (scrolled) { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - } - - // Synthesize key up from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - mCursorMotionAccumulator.finishSync(); - mCursorScrollAccumulator.finishSync(); -} - -int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); - } else { - return AKEY_STATE_UNKNOWN; - } -} - -void CursorInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -std::optional CursorInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - // If the device is orientationAware and not a mouse, - // it expects to dispatch events to any display - return std::make_optional(ADISPLAY_ID_NONE); - } - } - return std::nullopt; -} - -// --- RotaryEncoderInputMapper --- - -RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) - : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { - mSource = AINPUT_SOURCE_ROTARY_ENCODER; -} - -RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} - -uint32_t RotaryEncoderInputMapper::getSources() { - return mSource; -} - -void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { - float res = 0.0f; - if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { - ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); - } - if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), - mScalingFactor)) { - ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," - "default to 1.0!\n"); - mScalingFactor = 1.0f; - } - info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - res * mScalingFactor); - } -} - -void RotaryEncoderInputMapper::dump(std::string& dump) { - dump += INDENT2 "Rotary Encoder Input Mapper:\n"; - dump += StringPrintf(INDENT3 "HaveWheel: %s\n", - toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); -} - -void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - if (!changes) { - mRotaryEncoderScrollAccumulator.configure(getDevice()); - } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; - } - } -} - -void RotaryEncoderInputMapper::reset(nsecs_t when) { - mRotaryEncoderScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { - mRotaryEncoderScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void RotaryEncoderInputMapper::sync(nsecs_t when) { - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); - bool scrolled = scroll != 0; - - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - - // Send motion event. - if (scrolled) { - int32_t metaState = mContext->getGlobalMetaState(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - - mRotaryEncoderScrollAccumulator.finishSync(); -} - -// --- TouchInputMapper --- - -TouchInputMapper::TouchInputMapper(InputDevice* device) - : InputMapper(device), - mSource(0), - mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceWidth(-1), - mSurfaceHeight(-1), - mSurfaceLeft(0), - mSurfaceTop(0), - mPhysicalWidth(-1), - mPhysicalHeight(-1), - mPhysicalLeft(0), - mPhysicalTop(0), - mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} - -TouchInputMapper::~TouchInputMapper() {} - -uint32_t TouchInputMapper::getSources() { - return mSource; -} - -void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mDeviceMode != DEVICE_MODE_DISABLED) { - info->addMotionRange(mOrientedRanges.x); - info->addMotionRange(mOrientedRanges.y); - info->addMotionRange(mOrientedRanges.pressure); - - if (mOrientedRanges.haveSize) { - info->addMotionRange(mOrientedRanges.size); - } - - if (mOrientedRanges.haveTouchSize) { - info->addMotionRange(mOrientedRanges.touchMajor); - info->addMotionRange(mOrientedRanges.touchMinor); - } - - if (mOrientedRanges.haveToolSize) { - info->addMotionRange(mOrientedRanges.toolMajor); - info->addMotionRange(mOrientedRanges.toolMinor); - } - - if (mOrientedRanges.haveOrientation) { - info->addMotionRange(mOrientedRanges.orientation); - } - - if (mOrientedRanges.haveDistance) { - info->addMotionRange(mOrientedRanges.distance); - } - - if (mOrientedRanges.haveTilt) { - info->addMotionRange(mOrientedRanges.tilt); - } - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - } - info->setButtonUnderPad(mParameters.hasButtonUnderPad); - } -} - -void TouchInputMapper::dump(std::string& dump) { - dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); - dumpParameters(dump); - dumpVirtualKeys(dump); - dumpRawPointerAxes(dump); - dumpCalibration(dump); - dumpAffineTransformation(dump); - dumpSurface(dump); - - dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); - dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); - dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); - dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); - dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); - dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); - dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); - dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); - dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); - dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); - dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); - dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); - dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); - - dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); - dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", - mLastRawState.rawPointerData.pointerCount); - for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { - const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " - "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " - "toolType=%d, isHovering=%s\n", - i, pointer.id, pointer.x, pointer.y, pointer.pressure, - pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, - pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, - pointer.distance, pointer.toolType, toString(pointer.isHovering)); - } - - dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", - mLastCookedState.buttonState); - dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", - mLastCookedState.cookedPointerData.pointerCount); - for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { - const PointerProperties& pointerProperties = - mLastCookedState.cookedPointerData.pointerProperties[i]; - const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " - "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " - "toolMinor=%0.3f, " - "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " - "toolType=%d, isHovering=%s\n", - i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), - pointerProperties.toolType, - toString(mLastCookedState.cookedPointerData.isHovering(i))); - } - - dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); - dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", - mExternalStylusFusionTimeout); - dump += INDENT3 "External Stylus State:\n"; - dumpStylusState(dump, mExternalStylusState); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); - dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); - dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); - dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); - dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); - dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); - } -} - -const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { - switch (deviceMode) { - case DEVICE_MODE_DISABLED: - return "disabled"; - case DEVICE_MODE_DIRECT: - return "direct"; - case DEVICE_MODE_UNSCALED: - return "unscaled"; - case DEVICE_MODE_NAVIGATION: - return "navigation"; - case DEVICE_MODE_POINTER: - return "pointer"; - } - return "unknown"; -} - -void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - mConfig = *config; - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - - // Configure common accumulators. - mCursorScrollAccumulator.configure(getDevice()); - mTouchButtonAccumulator.configure(getDevice()); - - // Configure absolute axis information. - configureRawPointerAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { - // Update location calibration to reflect current settings - updateAffineTransformation(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - // Update pointer speed. - mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - } - - bool resetNeeded = false; - if (!changes || - (changes & - (InputReaderConfiguration::CHANGE_DISPLAY_INFO | - InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | - InputReaderConfiguration::CHANGE_SHOW_TOUCHES | - InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { - // Configure device sources, surface dimensions, orientation and - // scaling factors. - configureSurface(when, &resetNeeded); - } - - if (changes && resetNeeded) { - // Send reset, unless this is the first time the device has been configured, - // in which case the reader will call reset itself after all mappers are ready. - getDevice()->notifyReset(when); - } -} - -void TouchInputMapper::resolveExternalStylusPresence() { - std::vector devices; - mContext->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { - resetExternalStylus(); - } -} - -void TouchInputMapper::configureParameters() { - // Use the pointer presentation mode for devices that do not support distinct - // multitouch. The spot-based presentation relies on being able to accurately - // locate two or more fingers on the touch pad. - mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) - ? Parameters::GESTURE_MODE_SINGLE_TOUCH - : Parameters::GESTURE_MODE_MULTI_TOUCH; - - String8 gestureModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), - gestureModeString)) { - if (gestureModeString == "single-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; - } else if (gestureModeString == "multi-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; - } else if (gestureModeString != "default") { - ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); - } - } - - if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { - // The device is a touch screen. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { - // The device is a pointing device like a track pad. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || - getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { - // The device is a cursor device with a touch pad attached. - // By default don't use the touch pad to move the pointer. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else { - // The device is a touch pad of unknown purpose. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } - - mParameters.hasButtonUnderPad = - getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); - - String8 deviceTypeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), - deviceTypeString)) { - if (deviceTypeString == "touchScreen") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (deviceTypeString == "touchPad") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else if (deviceTypeString == "touchNavigation") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; - } else if (deviceTypeString == "pointer") { - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (deviceTypeString != "default") { - ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); - } - } - - mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; - getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - mParameters.associatedDisplayIsExternal = false; - if (mParameters.orientationAware || - mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || - mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - mParameters.hasAssociatedDisplay = true; - if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); - String8 uniqueDisplayId; - getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - uniqueDisplayId); - mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); - } - } - if (getDevice()->getAssociatedDisplayPort()) { - mParameters.hasAssociatedDisplay = true; - } - - // Initial downs on external touch devices should wake the device. - // Normally we don't do this for internal touch screens to prevent them from waking - // up in your pocket but you can enable it using the input device configuration. - mParameters.wake = getDevice()->isExternal(); - getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); -} - -void TouchInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - - switch (mParameters.gestureMode) { - case Parameters::GESTURE_MODE_SINGLE_TOUCH: - dump += INDENT4 "GestureMode: single-touch\n"; - break; - case Parameters::GESTURE_MODE_MULTI_TOUCH: - dump += INDENT4 "GestureMode: multi-touch\n"; - break; - default: - assert(false); - } - - switch (mParameters.deviceType) { - case Parameters::DEVICE_TYPE_TOUCH_SCREEN: - dump += INDENT4 "DeviceType: touchScreen\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_PAD: - dump += INDENT4 "DeviceType: touchPad\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: - dump += INDENT4 "DeviceType: touchNavigation\n"; - break; - case Parameters::DEVICE_TYPE_POINTER: - dump += INDENT4 "DeviceType: pointer\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " - "displayId='%s'\n", - toString(mParameters.hasAssociatedDisplay), - toString(mParameters.associatedDisplayIsExternal), - mParameters.uniqueDisplayId.c_str()); - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); -} - -void TouchInputMapper::configureRawPointerAxes() { - mRawPointerAxes.clear(); -} - -void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { - dump += INDENT3 "Raw Touch Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); -} - -bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; -} - -/** - * Determine which DisplayViewport to use. - * 1. If display port is specified, return the matching viewport. If matching viewport not - * found, then return. - * 2. If a device has associated display, get the matching viewport by either unique id or by - * the display type (internal or external). - * 3. Otherwise, use a non-display viewport. - */ -std::optional TouchInputMapper::findViewport() { - if (mParameters.hasAssociatedDisplay) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // Check if uniqueDisplayId is specified in idc file. - if (!mParameters.uniqueDisplayId.empty()) { - return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); - } - - ViewportType viewportTypeToUse; - if (mParameters.associatedDisplayIsExternal) { - viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; - } else { - viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; - } - - std::optional viewport = - mConfig.getDisplayViewportByType(viewportTypeToUse); - if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { - ALOGW("Input device %s should be associated with external display, " - "fallback to internal one for the external viewport is not found.", - getDeviceName().c_str()); - viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return viewport; - } - - // No associated display, return a non-display viewport. - DisplayViewport newViewport; - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - newViewport.setNonDisplayViewport(rawWidth, rawHeight); - return std::make_optional(newViewport); -} - -void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { - int32_t oldDeviceMode = mDeviceMode; - - resolveExternalStylusPresence(); - - // Determine device mode. - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && - mConfig.pointerGesturesEnabled) { - mSource = AINPUT_SOURCE_MOUSE; - mDeviceMode = DEVICE_MODE_POINTER; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && - mParameters.hasAssociatedDisplay) { - mSource = AINPUT_SOURCE_TOUCHSCREEN; - mDeviceMode = DEVICE_MODE_DIRECT; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - if (hasExternalStylus()) { - mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { - mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; - mDeviceMode = DEVICE_MODE_NAVIGATION; - } else { - mSource = AINPUT_SOURCE_TOUCHPAD; - mDeviceMode = DEVICE_MODE_UNSCALED; - } - - // Ensure we have valid X and Y axes. - if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { - ALOGW("Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Get associated display dimensions. - std::optional newViewport = findViewport(); - if (!newViewport) { - ALOGI("Touch device '%s' could not query the properties of its associated " - "display. The device will be inoperable until the display size " - "becomes available.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - - bool viewportChanged = mViewport != *newViewport; - if (viewportChanged) { - mViewport = *newViewport; - - if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { - // Convert rotated viewport to natural surface coordinates. - int32_t naturalLogicalWidth, naturalLogicalHeight; - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; - switch (mViewport.orientation) { - case DISPLAY_ORIENTATION_90: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; - - mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - - mSurfaceOrientation = - mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; - } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mSurfaceWidth = rawWidth; - mSurfaceHeight = rawHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceOrientation = DISPLAY_ORIENTATION_0; - } - } - - // If moving between pointer modes, need to reset some state. - bool deviceModeChanged = mDeviceMode != oldDeviceMode; - if (deviceModeChanged) { - mOrientedRanges.clear(); - } - - // Create or update pointer controller if needed. - if (mDeviceMode == DEVICE_MODE_POINTER || - (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == nullptr || viewportChanged) { - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - } - } else { - mPointerController.clear(); - } - - if (viewportChanged || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " - "display id %d", - getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, - mSurfaceOrientation, mDeviceMode, mViewport.displayId); - - // Configure X and Y factors. - mXScale = float(mSurfaceWidth) / rawWidth; - mYScale = float(mSurfaceHeight) / rawHeight; - mXTranslate = -mSurfaceLeft; - mYTranslate = -mSurfaceTop; - mXPrecision = 1.0f / mXScale; - mYPrecision = 1.0f / mYScale; - - mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; - mOrientedRanges.x.source = mSource; - mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; - mOrientedRanges.y.source = mSource; - - configureVirtualKeys(); - - // Scale factor for terms that are not oriented in a particular axis. - // If the pixels are square then xScale == yScale otherwise we fake it - // by choosing an average. - mGeometricScale = avg(mXScale, mYScale); - - // Size of diagonal axis. - float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Size factors. - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; - } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; - } else { - mSizeScale = 0.0f; - } - - mOrientedRanges.haveTouchSize = true; - mOrientedRanges.haveToolSize = true; - mOrientedRanges.haveSize = true; - - mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; - mOrientedRanges.touchMajor.source = mSource; - mOrientedRanges.touchMajor.min = 0; - mOrientedRanges.touchMajor.max = diagonalSize; - mOrientedRanges.touchMajor.flat = 0; - mOrientedRanges.touchMajor.fuzz = 0; - mOrientedRanges.touchMajor.resolution = 0; - - mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; - mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; - - mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; - mOrientedRanges.toolMajor.source = mSource; - mOrientedRanges.toolMajor.min = 0; - mOrientedRanges.toolMajor.max = diagonalSize; - mOrientedRanges.toolMajor.flat = 0; - mOrientedRanges.toolMajor.fuzz = 0; - mOrientedRanges.toolMajor.resolution = 0; - - mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; - mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; - - mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; - mOrientedRanges.size.source = mSource; - mOrientedRanges.size.min = 0; - mOrientedRanges.size.max = 1.0; - mOrientedRanges.size.flat = 0; - mOrientedRanges.size.fuzz = 0; - mOrientedRanges.size.resolution = 0; - } else { - mSizeScale = 0.0f; - } - - // Pressure factors. - mPressureScale = 0; - float pressureMax = 1.0; - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || - mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { - if (mCalibration.havePressureScale) { - mPressureScale = mCalibration.pressureScale; - pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; - } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { - mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; - } - } - - mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; - mOrientedRanges.pressure.source = mSource; - mOrientedRanges.pressure.min = 0; - mOrientedRanges.pressure.max = pressureMax; - mOrientedRanges.pressure.flat = 0; - mOrientedRanges.pressure.fuzz = 0; - mOrientedRanges.pressure.resolution = 0; - - // Tilt - mTiltXCenter = 0; - mTiltXScale = 0; - mTiltYCenter = 0; - mTiltYScale = 0; - mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; - if (mHaveTilt) { - mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); - mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); - mTiltXScale = M_PI / 180; - mTiltYScale = M_PI / 180; - - mOrientedRanges.haveTilt = true; - - mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; - mOrientedRanges.tilt.source = mSource; - mOrientedRanges.tilt.min = 0; - mOrientedRanges.tilt.max = M_PI_2; - mOrientedRanges.tilt.flat = 0; - mOrientedRanges.tilt.fuzz = 0; - mOrientedRanges.tilt.resolution = 0; - } - - // Orientation - mOrientationScale = 0; - if (mHaveTilt) { - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI; - mOrientedRanges.orientation.max = M_PI; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } else if (mCalibration.orientationCalibration != - Calibration::ORIENTATION_CALIBRATION_NONE) { - if (mCalibration.orientationCalibration == - Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { - if (mRawPointerAxes.orientation.valid) { - if (mRawPointerAxes.orientation.maxValue > 0) { - mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; - } else if (mRawPointerAxes.orientation.minValue < 0) { - mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; - } else { - mOrientationScale = 0; - } - } - } - - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } - - // Distance - mDistanceScale = 0; - if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { - if (mCalibration.haveDistanceScale) { - mDistanceScale = mCalibration.distanceScale; - } else { - mDistanceScale = 1.0f; - } - } - - mOrientedRanges.haveDistance = true; - - mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; - mOrientedRanges.distance.source = mSource; - mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; - mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; - mOrientedRanges.distance.flat = 0; - mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; - mOrientedRanges.distance.resolution = 0; - } - - // Compute oriented precision, scales and ranges. - // Note that the maximum value reported is an inclusive maximum value so it is one - // unit less than the total width or height of surface. - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - case DISPLAY_ORIENTATION_270: - mOrientedXPrecision = mYPrecision; - mOrientedYPrecision = mXPrecision; - - mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - - mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; - break; - - default: - mOrientedXPrecision = mXPrecision; - mOrientedYPrecision = mYPrecision; - - mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - - mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; - break; - } - - // Location - updateAffineTransformation(); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Scale movements such that one whole swipe of the touch pad covers a - // given area relative to the diagonal size of the display when no acceleration - // is applied. - // Assume that the touch pad has a square aspect ratio such that movements in - // X and Y of the same number of raw units cover the same physical distance. - mPointerXMovementScale = - mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; - mPointerYMovementScale = mPointerXMovementScale; - - // Scale zooms to cover a smaller range of the display than movements do. - // This value determines the area around the pointer that is affected by freeform - // pointer gestures. - mPointerXZoomScale = - mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; - mPointerYZoomScale = mPointerXZoomScale; - - // Max width between pointers to detect a swipe gesture is more than some fraction - // of the diagonal axis of the touch pad. Touches that are wider than this are - // translated into freeform gestures. - mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - - // Abort current pointer usages because the state has changed. - abortPointerUsage(when, 0 /*policyFlags*/); - } - - // Inform the dispatcher about the changes. - *outResetNeeded = true; - bumpGeneration(); - } -} - -void TouchInputMapper::dumpSurface(std::string& dump) { - dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); - dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); - dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); - dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); - dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); -} - -void TouchInputMapper::configureVirtualKeys() { - std::vector virtualKeyDefinitions; - getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); - - mVirtualKeys.clear(); - - if (virtualKeyDefinitions.size() == 0) { - return; - } - - int32_t touchScreenLeft = mRawPointerAxes.x.minValue; - int32_t touchScreenTop = mRawPointerAxes.y.minValue; - int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); - int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); - - for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { - VirtualKey virtualKey; - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - int32_t dummyKeyMetaState; - uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, - &dummyKeyMetaState, &flags)) { - ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); - continue; // drop the key - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + - touchScreenLeft; - virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + - touchScreenLeft; - virtualKey.hitTop = - (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + - touchScreenTop; - virtualKey.hitBottom = - (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + - touchScreenTop; - mVirtualKeys.push_back(virtualKey); - } -} - -void TouchInputMapper::dumpVirtualKeys(std::string& dump) { - if (!mVirtualKeys.empty()) { - dump += INDENT3 "Virtual Keys:\n"; - - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; - dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, - virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const PropertyMap& in = getDevice()->getConfiguration(); - Calibration& out = mCalibration; - - // Size - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { - if (sizeCalibrationString == "none") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } else if (sizeCalibrationString == "geometric") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } else if (sizeCalibrationString == "diameter") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; - } else if (sizeCalibrationString == "box") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; - } else if (sizeCalibrationString == "area") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; - } else if (sizeCalibrationString != "default") { - ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); - } - } - - out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); - out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); - out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { - if (pressureCalibrationString == "none") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } else if (pressureCalibrationString == "physical") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } else if (pressureCalibrationString == "amplitude") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else if (pressureCalibrationString != "default") { - ALOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); - } - } - - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { - if (orientationCalibrationString == "none") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } else if (orientationCalibrationString == "interpolated") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else if (orientationCalibrationString == "vector") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; - } else if (orientationCalibrationString != "default") { - ALOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); - } - } - - // Distance - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; - String8 distanceCalibrationString; - if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { - if (distanceCalibrationString == "none") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } else if (distanceCalibrationString == "scaled") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } else if (distanceCalibrationString != "default") { - ALOGW("Invalid value for touch.distance.calibration: '%s'", - distanceCalibrationString.string()); - } - } - - out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); - - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; - String8 coverageCalibrationString; - if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { - if (coverageCalibrationString == "none") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } else if (coverageCalibrationString == "box") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; - } else if (coverageCalibrationString != "default") { - ALOGW("Invalid value for touch.coverage.calibration: '%s'", - coverageCalibrationString.string()); - } - } -} - -void TouchInputMapper::resolveCalibration() { - // Size - if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } - } else { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } - - // Pressure - if (mRawPointerAxes.pressure.valid) { - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } - } else { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } - - // Orientation - if (mRawPointerAxes.orientation.valid) { - if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } - } else { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } - - // Distance - if (mRawPointerAxes.distance.valid) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } - } else { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } - - // Coverage - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { - mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } -} - -void TouchInputMapper::dumpCalibration(std::string& dump) { - dump += INDENT3 "Calibration:\n"; - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump += INDENT4 "touch.size.calibration: none\n"; - break; - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - dump += INDENT4 "touch.size.calibration: geometric\n"; - break; - case Calibration::SIZE_CALIBRATION_DIAMETER: - dump += INDENT4 "touch.size.calibration: diameter\n"; - break; - case Calibration::SIZE_CALIBRATION_BOX: - dump += INDENT4 "touch.size.calibration: box\n"; - break; - case Calibration::SIZE_CALIBRATION_AREA: - dump += INDENT4 "touch.size.calibration: area\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveSizeScale) { - dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); - } - - if (mCalibration.haveSizeBias) { - dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); - } - - if (mCalibration.haveSizeIsSummed) { - dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", - toString(mCalibration.sizeIsSummed)); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump += INDENT4 "touch.pressure.calibration: none\n"; - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump += INDENT4 "touch.pressure.calibration: physical\n"; - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump += INDENT4 "touch.pressure.calibration: amplitude\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.havePressureScale) { - dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump += INDENT4 "touch.orientation.calibration: none\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump += INDENT4 "touch.orientation.calibration: interpolated\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: - dump += INDENT4 "touch.orientation.calibration: vector\n"; - break; - default: - ALOG_ASSERT(false); - } - - // Distance - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_NONE: - dump += INDENT4 "touch.distance.calibration: none\n"; - break; - case Calibration::DISTANCE_CALIBRATION_SCALED: - dump += INDENT4 "touch.distance.calibration: scaled\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveDistanceScale) { - dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); - } - - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_NONE: - dump += INDENT4 "touch.coverage.calibration: none\n"; - break; - case Calibration::COVERAGE_CALIBRATION_BOX: - dump += INDENT4 "touch.coverage.calibration: box\n"; - break; - default: - ALOG_ASSERT(false); - } -} - -void TouchInputMapper::dumpAffineTransformation(std::string& dump) { - dump += INDENT3 "Affine Transformation:\n"; - - dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); - dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); - dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); - dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); - dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); - dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); -} - -void TouchInputMapper::updateAffineTransformation() { - mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), - mSurfaceOrientation); -} - -void TouchInputMapper::reset(nsecs_t when) { - mCursorButtonAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - mTouchButtonAccumulator.reset(getDevice()); - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mRawStatesPending.clear(); - mCurrentRawState.clear(); - mCurrentCookedState.clear(); - mLastRawState.clear(); - mLastCookedState.clear(); - mPointerUsage = POINTER_USAGE_NONE; - mSentHoverEnter = false; - mHavePointerIds = false; - mCurrentMotionAborted = false; - mDownTime = 0; - - mCurrentVirtualKey.down = false; - - mPointerGesture.reset(); - mPointerSimple.reset(); - resetExternalStylus(); - - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } - - InputMapper::reset(when); -} - -void TouchInputMapper::resetExternalStylus() { - mExternalStylusState.clear(); - mExternalStylusId = -1; - mExternalStylusFusionTimeout = LLONG_MAX; - mExternalStylusDataPending = false; -} - -void TouchInputMapper::clearStylusDataPendingFlags() { - mExternalStylusDataPending = false; - mExternalStylusFusionTimeout = LLONG_MAX; -} - -void TouchInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void TouchInputMapper::sync(nsecs_t when) { - const RawState* last = - mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); - - // Push a new state. - mRawStatesPending.emplace_back(); - - RawState* next = &mRawStatesPending.back(); - next->clear(); - next->when = when; - - // Sync button state. - next->buttonState = - mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); - - // Sync scroll - next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); - next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); - mCursorScrollAccumulator.finishSync(); - - // Sync touch - syncTouch(when, next); - - // Assign pointer ids. - if (!mHavePointerIds) { - assignPointerIds(last, next); - } - -#if DEBUG_RAW_EVENTS - ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x", - last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, - last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, - last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); -#endif - - processRawTouches(false /*timeout*/); -} - -void TouchInputMapper::processRawTouches(bool timeout) { - if (mDeviceMode == DEVICE_MODE_DISABLED) { - // Drop all input if the device is disabled. - mCurrentRawState.clear(); - mRawStatesPending.clear(); - return; - } - - // Drain any pending touch states. The invariant here is that the mCurrentRawState is always - // valid and must go through the full cook and dispatch cycle. This ensures that anything - // touching the current state will only observe the events that have been dispatched to the - // rest of the pipeline. - const size_t N = mRawStatesPending.size(); - size_t count; - for (count = 0; count < N; count++) { - const RawState& next = mRawStatesPending[count]; - - // A failure to assign the stylus id means that we're waiting on stylus data - // and so should defer the rest of the pipeline. - if (assignExternalStylusId(next, timeout)) { - break; - } - - // All ready to go. - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); - if (mCurrentRawState.when < mLastRawState.when) { - mCurrentRawState.when = mLastRawState.when; - } - cookAndDispatch(mCurrentRawState.when); - } - if (count != 0) { - mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); - } - - if (mExternalStylusDataPending) { - if (timeout) { - nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, synthesizing event with new stylus data"); -#endif - cookAndDispatch(when); - } else if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::cookAndDispatch(nsecs_t when) { - // Always start with a clean state. - mCurrentCookedState.clear(); - - // Apply stylus buttons to current raw state. - applyExternalStylusButtonState(when); - - // Handle policy on initial down or hover events. - bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && - mCurrentRawState.rawPointerData.pointerCount != 0; - - uint32_t policyFlags = 0; - bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; - if (initialDown || buttonsPressed) { - // If this is a touch screen, hide the pointer on an initial down. - if (mDeviceMode == DEVICE_MODE_DIRECT) { - getContext()->fadePointer(); - } - - if (mParameters.wake) { - policyFlags |= POLICY_FLAG_WAKE; - } - } - - // Consume raw off-screen touches before cooking pointer data. - // If touches are consumed, subsequent code will not receive any pointer data. - if (consumeRawTouches(when, policyFlags)) { - mCurrentRawState.rawPointerData.clear(); - } - - // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure - // with cooked pointer data that has the same ids and indices as the raw data. - // The following code can use either the raw or cooked data, as needed. - cookPointerData(); - - // Apply stylus pressure to current cooked state. - applyExternalStylusTouchState(when); - - // Synthesize key down from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); - - // Dispatch the touches either directly or by translation through a pointer on screen. - if (mDeviceMode == DEVICE_MODE_POINTER) { - for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mCurrentCookedState.fingerIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { - mCurrentCookedState.mouseIdBits.markBit(id); - } - } - for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } - } - - // Stylus takes precedence over all tools, then mouse, then finger. - PointerUsage pointerUsage = mPointerUsage; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - mCurrentCookedState.mouseIdBits.clear(); - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_STYLUS; - } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_MOUSE; - } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || - isPointerDown(mCurrentRawState.buttonState)) { - pointerUsage = POINTER_USAGE_GESTURES; - } - - dispatchPointerUsage(when, policyFlags, pointerUsage); - } else { - if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && - mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, - mViewport.displayId); - } - - if (!mCurrentMotionAborted) { - dispatchButtonRelease(when, policyFlags); - dispatchHoverExit(when, policyFlags); - dispatchTouches(when, policyFlags); - dispatchHoverEnterAndMove(when, policyFlags); - dispatchButtonPress(when, policyFlags); - } - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentMotionAborted = false; - } - } - - // Synthesize key up from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); - - // Clear some transient state. - mCurrentRawState.rawVScroll = 0; - mCurrentRawState.rawHScroll = 0; - - // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); -} - -void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; - } -} - -void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { - CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; - const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; - - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - - PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } - } -} - -bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { - if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { - return false; - } - - const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && - state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { -#if DEBUG_STYLUS_FUSION - ALOGD("Have both stylus and touch data, beginning fusion"); -#endif - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, assuming touch is not a stylus."); -#endif - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } -#if DEBUG_STYLUS_FUSION - ALOGD("No stylus data but stylus is connected, requesting timeout " - "(%" PRId64 "ms)", - mExternalStylusFusionTimeout); -#endif - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } - } - - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { -#if DEBUG_STYLUS_FUSION - ALOGD("Stylus pointer is going up"); -#endif - mExternalStylusId = -1; - } - - return false; -} - -void TouchInputMapper::timeoutExpired(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - if (mPointerUsage == POINTER_USAGE_GESTURES) { - dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); - } - } else if (mDeviceMode == DEVICE_MODE_DIRECT) { - if (mExternalStylusFusionTimeout < when) { - processRawTouches(true /*timeout*/); - } else if (mExternalStylusFusionTimeout != LLONG_MAX) { - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::updateExternalStylusState(const StylusState& state) { - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. - mExternalStylusDataPending = true; - processRawTouches(false /*timeout*/); - } -} - -bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { - // Check for release of a virtual key. - if (mCurrentVirtualKey.down) { - if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer went up while virtual key was down. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - return true; - } - - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation but do not consume the touch yet. - // This is useful when the user swipes through from the virtual key area - // into the main display surface. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, - mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | - AKEY_EVENT_FLAG_CANCELED); - } - } - - if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer just went down. Check for virtual key press or off-screen touches. - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (!isPointInsideSurface(pointer.x, pointer.y)) { - // If exactly one pointer went down, check for virtual key hit. - // Otherwise we will drop the entire stroke. - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey) { - mCurrentVirtualKey.down = true; - mCurrentVirtualKey.downTime = when; - mCurrentVirtualKey.keyCode = virtualKey->keyCode; - mCurrentVirtualKey.scanCode = virtualKey->scanCode; - mCurrentVirtualKey.ignored = - mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, - virtualKey->scanCode); - - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | - AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - } - } - return true; - } - } - - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch within the screen area. The idea is to filter out stray - // virtual key presses when interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mConfig.virtualKeyQuietTime > 0 && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); - } - return false; -} - -void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - int32_t keyCode = mCurrentVirtualKey.keyCode; - int32_t scanCode = mCurrentVirtualKey.scanCode; - nsecs_t downTime = mCurrentVirtualKey.downTime; - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, - scanCode, metaState, downTime); - getListener()->notifyKey(&args); -} - -void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - if (!currentIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mCurrentMotionAborted = true; - } -} - -void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - if (currentIdBits == lastIdBits) { - if (!currentIdBits.isEmpty()) { - // No pointer id changes so this is a move event. - // The listener takes care of batching moves so we don't have to deal with that here. - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } else { - // 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 moveIdBits(lastIdBits.value & currentIdBits.value); - BitSet32 dispatchedIdBits(lastIdBits.value); - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool moveNeeded = - updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, moveIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - - // Dispatch pointer up events. - while (!upIdBits.isEmpty()) { - uint32_t upId = upIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - dispatchedIdBits.clearBit(upId); - } - - // 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 && !moveIdBits.isEmpty()) { - ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - - // Dispatch pointer down events using the new pointer locations. - while (!downIdBits.isEmpty()) { - uint32_t downId = downIdBits.clearFirstMarkedBit(); - dispatchedIdBits.markBit(downId); - - if (dispatchedIdBits.count() == 1) { - // First pointer is going down. Set down time. - mDownTime = when; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, - downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } -} - -void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { - if (mSentHoverEnter && - (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || - !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { - int32_t metaState = getContext()->getGlobalMetaState(); - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, - mLastCookedState.buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, - mOrientedYPrecision, mDownTime); - mSentHoverEnter = false; - } -} - -void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { - if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && - !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - if (!mSentHoverEnter) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = true; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { - BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!releasedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); - buttonState &= ~actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { - BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!pressedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); - buttonState |= actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { - if (!cookedPointerData.touchingIdBits.isEmpty()) { - return cookedPointerData.touchingIdBits; - } - return cookedPointerData.hoveringIdBits; -} - -void TouchInputMapper::cookPointerData() { - uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; - - mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; - mCurrentCookedState.cookedPointerData.hoveringIdBits = - mCurrentRawState.rawPointerData.hoveringIdBits; - mCurrentCookedState.cookedPointerData.touchingIdBits = - mCurrentRawState.rawPointerData.touchingIdBits; - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentCookedState.buttonState = 0; - } else { - mCurrentCookedState.buttonState = mCurrentRawState.buttonState; - } - - // Walk through the the active pointers and map device coordinates onto - // surface coordinates and adjust for display orientation. - for (uint32_t i = 0; i < currentPointerCount; i++) { - const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; - - // Size - float touchMajor, touchMinor, toolMajor, toolMinor, size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - case Calibration::SIZE_CALIBRATION_DIAMETER: - case Calibration::SIZE_CALIBRATION_BOX: - case Calibration::SIZE_CALIBRATION_AREA: - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { - touchMajor = in.touchMajor; - touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - toolMajor = in.toolMajor; - toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.touchMajor.valid) { - toolMajor = touchMajor = in.touchMajor; - toolMinor = touchMinor = - mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.toolMajor.valid) { - touchMajor = toolMajor = in.toolMajor; - touchMinor = toolMinor = - mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; - } else { - ALOG_ASSERT(false, - "No touch or tool axes. " - "Size calibration should have been resolved to NONE."); - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - } - - if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { - uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); - if (touchingCount > 1) { - touchMajor /= touchingCount; - touchMinor /= touchingCount; - toolMajor /= touchingCount; - toolMinor /= touchingCount; - size /= touchingCount; - } - } - - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { - touchMajor *= mGeometricScale; - touchMinor *= mGeometricScale; - toolMajor *= mGeometricScale; - toolMinor *= mGeometricScale; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { - touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; - touchMinor = touchMajor; - toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; - toolMinor = toolMajor; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { - touchMinor = touchMajor; - toolMinor = toolMajor; - } - - mCalibration.applySizeScaleAndBias(&touchMajor); - mCalibration.applySizeScaleAndBias(&touchMinor); - mCalibration.applySizeScaleAndBias(&toolMajor); - mCalibration.applySizeScaleAndBias(&toolMinor); - size *= mSizeScale; - break; - default: - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - break; - } - - // Pressure - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = in.pressure * mPressureScale; - break; - default: - pressure = in.isHovering ? 0 : 1; - break; - } - - // Tilt and Orientation - float tilt; - float orientation; - if (mHaveTilt) { - float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; - float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); - tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); - } else { - tilt = 0; - - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mOrientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float confidence = hypotf(c1, c2); - float scale = 1.0f + confidence / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; - } else { - orientation = 0; - } - break; - } - default: - orientation = 0; - } - } - - // Distance - float distance; - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_SCALED: - distance = in.distance * mDistanceScale; - break; - default: - distance = 0; - } - - // Coverage - int32_t rawLeft, rawTop, rawRight, rawBottom; - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_BOX: - rawLeft = (in.toolMinor & 0xffff0000) >> 16; - rawRight = in.toolMinor & 0x0000ffff; - rawBottom = in.toolMajor & 0x0000ffff; - rawTop = (in.toolMajor & 0xffff0000) >> 16; - break; - default: - rawLeft = rawTop = rawRight = rawBottom = 0; - break; - } - - // Adjust X,Y coords for device calibration - // TODO: Adjust coverage coords? - float xTransformed = in.x, yTransformed = in.y; - mAffineTransform.applyTo(xTransformed, yTransformed); - - // Adjust X, Y, and coverage coords for surface orientation. - float x, y; - float left, top, right, bottom; - - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; - left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; - top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; - orientation -= M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { - orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; - y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; - left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; - right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; - bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; - top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; - orientation -= M_PI; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { - orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; - y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; - right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; - bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - orientation += M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation > mOrientedRanges.orientation.max) { - orientation -= - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - default: - x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - break; - } - - // Write output coords. - PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; - out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); - out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); - out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); - } else { - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); - } - - // Write output properties. - PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; - uint32_t id = in.id; - properties.clear(); - properties.id = id; - properties.toolType = in.toolType; - - // Write id index. - mCurrentCookedState.cookedPointerData.idToIndex[id] = i; - } -} - -void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, - PointerUsage pointerUsage) { - if (pointerUsage != mPointerUsage) { - abortPointerUsage(when, policyFlags); - mPointerUsage = pointerUsage; - } - - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); - break; - case POINTER_USAGE_STYLUS: - dispatchPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - dispatchPointerMouse(when, policyFlags); - break; - default: - break; - } -} - -void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - abortPointerGestures(when, policyFlags); - break; - case POINTER_USAGE_STYLUS: - abortPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - abortPointerMouse(when, policyFlags); - break; - default: - break; - } - - mPointerUsage = POINTER_USAGE_NONE; -} - -void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { - // Update current gesture coordinates. - bool cancelPreviousGesture, finishPreviousGesture; - bool sendEvents = - preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); - if (!sendEvents) { - return; - } - if (finishPreviousGesture) { - cancelPreviousGesture = false; - } - - // Update the pointer presentation and spots. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - if (finishPreviousGesture || cancelPreviousGesture) { - mPointerController->clearSpots(); - } - - if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, - mPointerController->getDisplayId()); - } - } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - } - - // Show or hide the pointer if needed. - switch (mPointerGesture.currentGestureMode) { - case PointerGesture::NEUTRAL: - case PointerGesture::QUIET: - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && - mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { - // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); - } - break; - case PointerGesture::TAP: - case PointerGesture::TAP_DRAG: - case PointerGesture::BUTTON_CLICK_OR_DRAG: - case PointerGesture::HOVER: - case PointerGesture::PRESS: - case PointerGesture::SWIPE: - // Unfade the pointer when the current gesture manipulates the - // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - break; - case PointerGesture::FREEFORM: - // Fade the pointer when the current gesture manipulates a different - // area and there are spots to guide the user experience. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - break; - } - - // Send events! - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || - mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || - mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || - mPointerGesture.currentGestureMode == PointerGesture::PRESS || - mPointerGesture.currentGestureMode == PointerGesture::SWIPE || - mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; - bool moveNeeded = false; - if (down && !cancelPreviousGesture && !finishPreviousGesture && - !mPointerGesture.lastGestureIdBits.isEmpty() && - !mPointerGesture.currentGestureIdBits.isEmpty()) { - BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & - mPointerGesture.lastGestureIdBits.value); - moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - } - - // Send motion events for all pointers that went up or were canceled. - BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); - if (!dispatchedGestureIdBits.isEmpty()) { - if (cancelPreviousGesture) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - - dispatchedGestureIdBits.clear(); - } else { - BitSet32 upGestureIdBits; - if (finishPreviousGesture) { - upGestureIdBits = dispatchedGestureIdBits; - } else { - upGestureIdBits.value = - dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; - } - while (!upGestureIdBits.isEmpty()) { - uint32_t id = upGestureIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); - - dispatchedGestureIdBits.clearBit(id); - } - } - } - - // Send motion events for all pointers that moved. - if (moveNeeded) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - } - - // Send motion events for all pointers that went down. - if (down) { - BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & - ~dispatchedGestureIdBits.value); - while (!downGestureIdBits.isEmpty()) { - uint32_t id = downGestureIdBits.clearFirstMarkedBit(); - dispatchedGestureIdBits.markBit(id); - - if (dispatchedGestureIdBits.count() == 1) { - mPointerGesture.downTime = when; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); - } - } - - // Send motion events for hover. - if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); - } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { - // Synthesize a hover move event after all pointers go up to indicate that - // the pointer is hovering again even if the user is not currently touching - // the touch pad. This ensures that a view will receive a fresh hover enter - // event after a tap. - float x, y; - mPointerController->getPosition(&x, &y); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - PointerCoords pointerCoords; - pointerCoords.clear(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - - const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Update state. - mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; - if (!down) { - mPointerGesture.lastGestureIdBits.clear(); - } else { - mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; - for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - mPointerGesture.lastGestureProperties[index].copyFrom( - mPointerGesture.currentGestureProperties[index]); - mPointerGesture.lastGestureCoords[index].copyFrom( - mPointerGesture.currentGestureCoords[index]); - mPointerGesture.lastGestureIdToIndex[id] = index; - } - } -} - -void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { - // Cancel previously dispatches pointers. - if (!mPointerGesture.lastGestureIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } - - // Reset the current pointer gesture. - mPointerGesture.reset(); - mPointerVelocityControl.reset(); - - // Remove any current spots. - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } -} - -bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, - bool* outFinishPreviousGesture, bool isTimeout) { - *outCancelPreviousGesture = false; - *outFinishPreviousGesture = false; - - // Handle TAP timeout. - if (isTimeout) { -#if DEBUG_GESTURES - ALOGD("Gestures: Processing timeout"); -#endif - - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - // The tap/drag timeout has not yet expired. - getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + - mConfig.pointerGestureTapDragInterval); - } else { - // The tap is finished. -#if DEBUG_GESTURES - ALOGD("Gestures: TAP finished"); -#endif - *outFinishPreviousGesture = true; - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - return true; - } - } - - // We did not handle this timeout. - return false; - } - - const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); - const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); - - // Update the velocity tracker. - { - VelocityTracker::Position positions[MAX_POINTERS]; - uint32_t count = 0; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - positions[count].x = pointer.x * mPointerXMovementScale; - positions[count].y = pointer.y * mPointerYMovementScale; - } - mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - positions); - } - - // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning - // to NEUTRAL, then we should not generate tap event. - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && - mPointerGesture.lastGestureMode != PointerGesture::TAP && - mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { - mPointerGesture.resetTap(); - } - - // Pick a new active touch id if needed. - // Choose an arbitrary pointer that just went down, if there is one. - // Otherwise choose an arbitrary remaining pointer. - // This guarantees we always have an active touch id when there is at least one pointer. - // We keep the same active touch id for as long as possible. - int32_t lastActiveTouchId = mPointerGesture.activeTouchId; - int32_t activeTouchId = lastActiveTouchId; - if (activeTouchId < 0) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - mPointerGesture.firstTouchTime = when; - } - } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = false; - if (activeTouchId < 0) { - mPointerGesture.resetQuietTime(); - } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || - mPointerGesture.lastGestureMode == PointerGesture::SWIPE || - mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && - currentFingerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && - currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; - } - } - } - - // Switch states based on button and pointer state. - if (isQuietTime) { - // Case 1: Quiet time. (QUIET) -#if DEBUG_GESTURES - ALOGD("Gestures: QUIET for next %0.3fms", - (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { - *outFinishPreviousGesture = true; - } - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::QUIET; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - } else if (isPointerDown(mCurrentRawState.buttonState)) { - // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) - // The pointer follows the active touch point. - // Emit DOWN, MOVE, UP events at the pointer location. - // - // Only the active touch matters; other fingers are ignored. This policy helps - // to handle the case where the user places a second finger on the touch pad - // to apply the necessary force to depress an integrated button below the surface. - // We don't want the second finger to be delivered to applications. - // - // For this to work well, we need to make sure to track the pointer that is really - // active. If the user first puts one finger down to click then adds another - // finger to drag then the active pointer should switch to the finger that is - // being dragged. -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " - "currentFingerCount=%d", - activeTouchId, currentFingerCount); -#endif - // Reset state when just starting. - if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { - *outFinishPreviousGesture = true; - mPointerGesture.activeGestureId = 0; - } - - // Switch pointers if needed. - // Find the fastest pointer and follow it. - if (activeTouchId >= 0 && currentFingerCount > 1) { - int32_t bestId = -1; - float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); - if (speed > bestSpeed) { - bestId = id; - bestSpeed = speed; - } - } - } - if (bestId >= 0 && bestId != activeTouchId) { - mPointerGesture.activeTouchId = activeTouchId = bestId; -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " - "bestId=%d, bestSpeed=%0.3f", - bestId, bestSpeed); -#endif - } - } - - float deltaX = 0, deltaY = 0; - if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the click will occur at the position of the anchor - // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (currentFingerCount == 0) { - // Case 3. No fingers down and button is not pressed. (NEUTRAL) - if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { - *outFinishPreviousGesture = true; - } - - // Watch for taps coming out of HOVER or TAP_DRAG mode. - // Checking for taps after TAP_DRAG allows us to detect double-taps. - bool tapped = false; - if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || - mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && - lastFingerCount == 1) { - if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && - fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP"); -#endif - - mPointerGesture.tapUpTime = when; - getContext()->requestTimeoutAtTime(when + - mConfig.pointerGestureTapDragInterval); - - mPointerGesture.activeGestureId = 0; - mPointerGesture.currentGestureMode = PointerGesture::TAP; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = - mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.tapX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.tapY); - mPointerGesture.currentGestureCoords[0] - .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - tapped = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - if (mPointerGesture.tapDownTime != LLONG_MIN) { - ALOGD("Gestures: Not a TAP, %0.3fms since down", - (when - mPointerGesture.tapDownTime) * 0.000001f); - } else { - ALOGD("Gestures: Not a TAP, incompatible mode transitions"); - } -#endif - } - } - - mPointerVelocityControl.reset(); - - if (!tapped) { -#if DEBUG_GESTURES - ALOGD("Gestures: NEUTRAL"); -#endif - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - } - } else if (currentFingerCount == 1) { - // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) - // The pointer follows the active touch point. - // When in HOVER, emit HOVER_MOVE events at the pointer location. - // When in TAP_DRAG, emit MOVE events at the pointer location. - ALOG_ASSERT(activeTouchId >= 0); - - mPointerGesture.currentGestureMode = PointerGesture::HOVER; - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && - fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", - (when - mPointerGesture.tapUpTime) * 0.000001f); -#endif - } - } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } - - float deltaX = 0, deltaY = 0; - if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - bool down; - if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP_DRAG"); -#endif - down = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: HOVER"); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { - *outFinishPreviousGesture = true; - } - mPointerGesture.activeGestureId = 0; - down = false; - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - down ? 1.0f : 0.0f); - - if (lastFingerCount == 0 && currentFingerCount != 0) { - mPointerGesture.resetTap(); - mPointerGesture.tapDownTime = when; - mPointerGesture.tapX = x; - mPointerGesture.tapY = y; - } - } else { - // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) - // We need to provide feedback for each finger that goes down so we cannot wait - // for the fingers to move before deciding what to do. - // - // The ambiguous case is deciding what to do when there are two fingers down but they - // have not moved enough to determine whether they are part of a drag or part of a - // freeform gesture, or just a press or long-press at the pointer location. - // - // When there are two fingers we start with the PRESS hypothesis and we generate a - // down at the pointer location. - // - // When the two fingers move enough or when additional fingers are added, we make - // a decision to transition into SWIPE or FREEFORM mode accordingly. - ALOG_ASSERT(activeTouchId >= 0); - - bool settled = when >= - mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; - if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && - mPointerGesture.lastGestureMode != PointerGesture::SWIPE && - mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - *outFinishPreviousGesture = true; - } else if (!settled && currentFingerCount > lastFingerCount) { - // Additional pointers have gone down but not yet settled. - // Reset the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - - when) * 0.000001f); -#endif - *outCancelPreviousGesture = true; - } else { - // Continue previous gesture. - mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; - } - - if (*outFinishPreviousGesture || *outCancelPreviousGesture) { - mPointerGesture.currentGestureMode = PointerGesture::PRESS; - mPointerGesture.activeGestureId = 0; - mPointerGesture.referenceIdBits.clear(); - mPointerVelocityControl.reset(); - - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - - when) * 0.000001f); -#endif - mCurrentRawState.rawPointerData - .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } - - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & - ~mPointerGesture.referenceIdBits.value); - !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; - - // Add delta for all fingers and calculate a common movement delta. - float commonDeltaX = 0, commonDeltaY = 0; - BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value); - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { - bool first = (idBits == commonIdBits); - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); - const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } - } - - // Consider transitions from PRESS to SWIPE or MULTITOUCH. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float dist[MAX_POINTER_ID + 1]; - int32_t distOverThreshold = 0; - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); - if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { - distOverThreshold += 1; - } - } - - // Only transition when at least two pointers have moved further than - // the minimum distance threshold. - if (distOverThreshold >= 2) { - if (currentFingerCount > 2) { - // There are more than two pointers, switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are exactly two pointers. - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - uint32_t id1 = idBits.clearFirstMarkedBit(); - uint32_t id2 = idBits.firstMarkedBit(); - const RawPointerData::Pointer& p1 = - mCurrentRawState.rawPointerData.pointerForId(id1); - const RawPointerData::Pointer& p2 = - mCurrentRawState.rawPointerData.pointerForId(id2); - float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); - if (mutualDistance > mPointerGestureMaxSwipeWidth) { - // There are two pointers but they are too far apart for a SWIPE, - // switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - mutualDistance, mPointerGestureMaxSwipeWidth); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - float dist1 = dist[id1]; - float dist2 = dist[id2]; - if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && - dist2 >= mConfig.pointerGestureMultitouchMinDistance) { - // Calculate the dot product of the displacement vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * - // mag(v2). - PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; - PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; - float dx1 = delta1.dx * mPointerXZoomScale; - float dy1 = delta1.dy * mPointerYZoomScale; - float dx2 = delta2.dx * mPointerXZoomScale; - float dy2 = delta2.dy * mPointerYZoomScale; - float dot = dx1 * dx2 + dy1 * dy2; - float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to SWIPE, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - } - } - } - } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // Switch from SWIPE to FREEFORM if additional pointers go down. - // Cancel previous gesture. - if (currentFingerCount > 2) { -#if DEBUG_GESTURES - ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - - // Move the reference points based on the overall group motion of the fingers - // except in PRESS mode while waiting for a transition to occur. - if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && - (commonDeltaX || commonDeltaY)) { - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; - - commonDeltaX *= mPointerXMovementScale; - commonDeltaY *= mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); - mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - } - - // Report gestures. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || - mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // PRESS or SWIPE mode. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - // FREEFORM mode. -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - - BitSet32 mappedTouchIdBits; - BitSet32 usedGestureIdBits; - if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - // Initially, assign the active gesture id to the active touch point - // if there is one. No other touch id bits are mapped yet. - if (!*outCancelPreviousGesture) { - mappedTouchIdBits.markBit(activeTouchId); - usedGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = - mPointerGesture.activeGestureId; - } else { - mPointerGesture.activeGestureId = -1; - } - } else { - // Otherwise, assume we mapped all touches from the previous frame. - // Reuse all mappings that are still applicable. - mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value; - usedGestureIdBits = mPointerGesture.lastGestureIdBits; - - // Check whether we need to choose a new active gesture id because the - // current went went up. - for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & - ~mCurrentCookedState.fingerIdBits.value); - !upTouchIdBits.isEmpty();) { - uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); - uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; - if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { - mPointerGesture.activeGestureId = -1; - break; - } - } - } - -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM follow up " - "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " - "activeGestureId=%d", - mappedTouchIdBits.value, usedGestureIdBits.value, - mPointerGesture.activeGestureId); -#endif - - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - for (uint32_t i = 0; i < currentFingerCount; i++) { - uint32_t touchId = idBits.clearFirstMarkedBit(); - uint32_t gestureId; - if (!mappedTouchIdBits.hasBit(touchId)) { - gestureId = usedGestureIdBits.markFirstUnmarkedBit(); - mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "new mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } else { - gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "existing mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } - mPointerGesture.currentGestureIdBits.markBit(gestureId); - mPointerGesture.currentGestureIdToIndex[gestureId] = i; - - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(touchId); - float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; - float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - - mPointerGesture.currentGestureProperties[i].clear(); - mPointerGesture.currentGestureProperties[i].id = gestureId; - mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[i].clear(); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX + deltaX); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY + deltaY); - mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - 1.0f); - } - - if (mPointerGesture.activeGestureId < 0) { - mPointerGesture.activeGestureId = - mPointerGesture.currentGestureIdBits.firstMarkedBit(); -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM new " - "activeGestureId=%d", - mPointerGesture.activeGestureId); -#endif - } - } - } - - mPointerController->setButtonState(mCurrentRawState.buttonState); - -#if DEBUG_GESTURES - ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGestureIdBits=0x%08x", - toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); - for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; - ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } - for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; - ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } -#endif - return true; -} - -void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); - uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); - float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); - mPointerController->setPosition(x, y); - - hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); - down = !hovering; - - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[index]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; - } else { - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); -} - -void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); - uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - float deltaX = 0, deltaY = 0; - if (mLastCookedState.mouseIdBits.hasBit(id)) { - uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - - mLastRawState.rawPointerData.pointers[lastIndex].x) * - mPointerXMovementScale; - deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - - mLastRawState.rawPointerData.pointers[lastIndex].y) * - mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - down = isPointerDown(mCurrentRawState.buttonState); - hovering = !down; - - float x, y; - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - hovering ? 0.0f : 1.0f); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; - } else { - mPointerVelocityControl.reset(); - - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); - - mPointerVelocityControl.reset(); -} - -void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, - bool hovering) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t displayId = mViewport.displayId; - - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); - - float xCursorPosition; - float yCursorPosition; - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - - if (mPointerSimple.down && !down) { - mPointerSimple.down = false; - - // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mPointerSimple.hovering && !hovering) { - mPointerSimple.hovering = false; - - // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (down) { - if (!mPointerSimple.down) { - mPointerSimple.down = true; - mPointerSimple.downTime = when; - - // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (hovering) { - if (!mPointerSimple.hovering) { - mPointerSimple.hovering = true; - - // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { - float vscroll = mCurrentRawState.rawVScroll; - float hscroll = mCurrentRawState.rawHScroll; - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - // Send scroll. - PointerCoords pointerCoords; - pointerCoords.copyFrom(mPointerSimple.currentCoords); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Save state. - if (down || hovering) { - mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); - mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); - } else { - mPointerSimple.reset(); - } -} - -void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - dispatchPointerSimple(when, policyFlags, false, false); -} - -void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, int32_t edgeFlags, - const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, - BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime) { - PointerCoords pointerCoords[MAX_POINTERS]; - PointerProperties pointerProperties[MAX_POINTERS]; - uint32_t pointerCount = 0; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = idToIndex[id]; - pointerProperties[pointerCount].copyFrom(properties[index]); - pointerCoords[pointerCount].copyFrom(coords[index]); - - if (changedId >= 0 && id == uint32_t(changedId)) { - action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - - pointerCount += 1; - } - - ALOG_ASSERT(pointerCount != 0); - - if (changedId >= 0 && pointerCount == 1) { - // Replace initial down and final up action. - // We can compare the action without masking off the changed pointer index - // because we know the index is 0. - if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { - action = AMOTION_EVENT_ACTION_DOWN; - } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { - action = AMOTION_EVENT_ACTION_UP; - } else { - // Can't happen. - ALOG_ASSERT(false); - } - } - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mDeviceMode == DEVICE_MODE_POINTER) { - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - } - const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); - const int32_t deviceId = getDeviceId(); - std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); - std::for_each(frames.begin(), frames.end(), - [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, - pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, - downTime, std::move(frames)); - getListener()->notifyMotion(&args); -} - -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, - const uint32_t* inIdToIndex, - PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; -} - -void TouchInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -void TouchInputMapper::cancelTouch(nsecs_t when) { - abortPointerUsage(when, 0 /*policyFlags*/); - abortTouches(when, 0 /* policyFlags*/); -} - -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - const float scaledX = x * mXScale; - const float scaledY = y * mYScale; - return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && - y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; -} - -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { - for (const VirtualKey& virtualKey : mVirtualKeys) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return &virtualKey; - } - } - - return nullptr; -} - -void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { - uint32_t currentPointerCount = current->rawPointerData.pointerCount; - uint32_t lastPointerCount = last->rawPointerData.pointerCount; - - current->rawPointerData.clearIdBits(); - - if (currentPointerCount == 0) { - // No pointers to assign. - return; - } - - if (lastPointerCount == 0) { - // All pointers are new. - for (uint32_t i = 0; i < currentPointerCount; i++) { - uint32_t id = i; - current->rawPointerData.pointers[i].id = id; - current->rawPointerData.idToIndex[id] = i; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); - } - return; - } - - if (currentPointerCount == 1 && lastPointerCount == 1 && - current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = last->rawPointerData.pointers[0].id; - current->rawPointerData.pointers[0].id = id; - current->rawPointerData.idToIndex[id] = 0; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); - return; - } - - // General case. - // 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. - // The pointers must have the same tool type but it is possible for them to - // transition from hovering to touching or vice-versa while retaining the same id. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - const RawPointerData::Pointer& currentPointer = - current->rawPointerData.pointers[currentPointerIndex]; - const RawPointerData::Pointer& lastPointer = - last->rawPointerData.pointers[lastPointerIndex]; - if (currentPointer.toolType == lastPointer.toolType) { - int64_t deltaX = currentPointer.x - lastPointer.x; - int64_t deltaY = currentPointer.y - lastPointer.y; - - 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; - } - } - } - - // 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; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, 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. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { - while (heapSize > 0) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0;;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize && - heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, - heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); - } -#endif - } - - heapSize -= 1; - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering( - currentPointerIndex)); - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 - ", distance=%" PRIu64, - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to pointers that were not matched in the process. - for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { - uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); - uint32_t id = usedIdBits.markFirstUnmarkedBit(); - - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); -#endif - } -} - -int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.keyCode == keyCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.scanCode == scanCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - for (const VirtualKey& virtualKey : mVirtualKeys) { - for (size_t i = 0; i < numCodes; i++) { - if (virtualKey.keyCode == keyCodes[i]) { - outFlags[i] = 1; - } - } - } - - return true; -} - -std::optional TouchInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - return std::make_optional(mViewport.displayId); - } - } - return std::nullopt; -} - -// --- SingleTouchInputMapper --- - -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} - -SingleTouchInputMapper::~SingleTouchInputMapper() {} - -void SingleTouchInputMapper::reset(nsecs_t when) { - mSingleTouchMotionAccumulator.reset(getDevice()); - - TouchInputMapper::reset(when); -} - -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mSingleTouchMotionAccumulator.process(rawEvent); -} - -void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - if (mTouchButtonAccumulator.isToolActive()) { - outState->rawPointerData.pointerCount = 1; - outState->rawPointerData.idToIndex[0] = 0; - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && - (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && - mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); - outState->rawPointerData.markIdBit(0, isHovering); - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; - outPointer.id = 0; - outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); - outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); - outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - outPointer.touchMajor = 0; - outPointer.touchMinor = 0; - outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.orientation = 0; - outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); - outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); - outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - outPointer.isHovering = isHovering; - } -} - -void SingleTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); - getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); -} - -bool SingleTouchInputMapper::hasStylus() const { - return mTouchButtonAccumulator.hasStylus(); -} - -// --- MultiTouchInputMapper --- - -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} - -MultiTouchInputMapper::~MultiTouchInputMapper() {} - -void MultiTouchInputMapper::reset(nsecs_t when) { - mMultiTouchMotionAccumulator.reset(getDevice()); - - mPointerIdBits.clear(); - - TouchInputMapper::reset(when); -} - -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mMultiTouchMotionAccumulator.process(rawEvent); -} - -void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); - size_t outCount = 0; - BitSet32 newPointerIdBits; - mHavePointerIds = true; - - for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const MultiTouchMotionAccumulator::Slot* inSlot = - mMultiTouchMotionAccumulator.getSlot(inIndex); - if (!inSlot->isInUse()) { - continue; - } - - if (outCount >= MAX_POINTERS) { -#if DEBUG_POINTERS - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); -#endif - break; // too many fingers! - } - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; - outPointer.x = inSlot->getX(); - outPointer.y = inSlot->getY(); - outPointer.pressure = inSlot->getPressure(); - outPointer.touchMajor = inSlot->getTouchMajor(); - outPointer.touchMinor = inSlot->getTouchMinor(); - outPointer.toolMajor = inSlot->getToolMajor(); - outPointer.toolMinor = inSlot->getToolMinor(); - outPointer.orientation = inSlot->getOrientation(); - outPointer.distance = inSlot->getDistance(); - outPointer.tiltX = 0; - outPointer.tiltY = 0; - - outPointer.toolType = inSlot->getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - } - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && - (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); - outPointer.isHovering = isHovering; - - // Assign pointer id using tracking id if available. - if (mHavePointerIds) { - int32_t trackingId = inSlot->getTrackingId(); - int32_t id = -1; - if (trackingId >= 0) { - for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { - uint32_t n = idBits.clearFirstMarkedBit(); - if (mPointerTrackingIdMap[n] == trackingId) { - id = n; - } - } - - if (id < 0 && !mPointerIdBits.isFull()) { - id = mPointerIdBits.markFirstUnmarkedBit(); - mPointerTrackingIdMap[id] = trackingId; - } - } - if (id < 0) { - mHavePointerIds = false; - outState->rawPointerData.clearIdBits(); - newPointerIdBits.clear(); - } else { - outPointer.id = id; - outState->rawPointerData.idToIndex[id] = outCount; - outState->rawPointerData.markIdBit(id, isHovering); - newPointerIdBits.markBit(id); - } - } - outCount += 1; - } - - outState->rawPointerData.pointerCount = outCount; - mPointerIdBits = newPointerIdBits; - - mMultiTouchMotionAccumulator.finishSync(); -} - -void MultiTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); - getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); - getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); - getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); - - if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && - mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { - size_t slotCount = mRawPointerAxes.slot.maxValue + 1; - if (slotCount > MAX_SLOTS) { - ALOGW("MultiTouch Device %s reported %zu slots but the framework " - "only supports a maximum of %zu slots at this time.", - getDeviceName().c_str(), slotCount, MAX_SLOTS); - slotCount = MAX_SLOTS; - } - mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); - } else { - mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, - false /*usingSlotsProtocol*/); - } -} - -bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); -} - -// --- ExternalStylusInputMapper - -ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} - -uint32_t ExternalStylusInputMapper::getSources() { - return AINPUT_SOURCE_STYLUS; -} - -void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f); -} - -void ExternalStylusInputMapper::dump(std::string& dump) { - dump += INDENT2 "External Stylus Input Mapper:\n"; - dump += INDENT3 "Raw Stylus Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); - dump += INDENT3 "Stylus State:\n"; - dumpStylusState(dump, mStylusState); -} - -void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDevice()); -} - -void ExternalStylusInputMapper::reset(nsecs_t when) { - InputDevice* device = getDevice(); - mSingleTouchMotionAccumulator.reset(device); - mTouchButtonAccumulator.reset(device); - InputMapper::reset(when); -} - -void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { - mSingleTouchMotionAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void ExternalStylusInputMapper::sync(nsecs_t when) { - mStylusState.clear(); - - mStylusState.when = when; - - mStylusState.toolType = mTouchButtonAccumulator.getToolType(); - if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; - } - - mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - - mContext->dispatchExternalStylusState(mStylusState); -} - -// --- JoystickInputMapper --- - -JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} - -JoystickInputMapper::~JoystickInputMapper() {} - -uint32_t JoystickInputMapper::getSources() { - return AINPUT_SOURCE_JOYSTICK; -} - -void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - for (size_t i = 0; i < mAxes.size(); i++) { - const Axis& axis = mAxes.valueAt(i); - addMotionRange(axis.axisInfo.axis, axis, info); - - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - addMotionRange(axis.axisInfo.highAxis, axis, info); - } - } -} - -void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { - info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, - axis.resolution); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to register - * the old axes as duplicates of their corresponding new ones. */ - int32_t compatAxis = getCompatAxis(axisId); - if (compatAxis >= 0) { - info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, - axis.fuzz, axis.resolution); - } -} - -/* A mapping from axes the joystick actually has to the axes that should be - * artificially created for compatibility purposes. - * Returns -1 if no compatibility axis is needed. */ -int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_LTRIGGER: - return AMOTION_EVENT_AXIS_BRAKE; - case AMOTION_EVENT_AXIS_RTRIGGER: - return AMOTION_EVENT_AXIS_GAS; - } - return -1; -} - -void JoystickInputMapper::dump(std::string& dump) { - dump += INDENT2 "Joystick Input Mapper:\n"; - - dump += INDENT3 "Axes:\n"; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - const char* label = getAxisLabel(axis.axisInfo.axis); - if (label) { - dump += StringPrintf(INDENT4 "%s", label); - } else { - dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - label = getAxisLabel(axis.axisInfo.highAxis); - if (label) { - dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); - } else { - dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, - axis.axisInfo.splitValue); - } - } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { - dump += " (invert)"; - } - - dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " - "highScale=%0.5f, highOffset=%0.5f\n", - axis.scale, axis.offset, axis.highScale, axis.highOffset); - dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " - "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", - mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, - axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, - axis.rawAxisInfo.resolution); - } -} - -void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Collect all axes. - for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { - continue; // axis must be claimed by a different device - } - - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { - // Map axis. - AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); - if (!explicitlyMapped) { - // Axis is not explicitly mapped, will choose a generic axis later. - axisInfo.mode = AxisInfo::MODE_NORMAL; - axisInfo.axis = -1; - } - - // Apply flat override. - int32_t rawFlat = - axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, - offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } - - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; - - mAxes.add(abs, axis); - } - } - - // If there are too many axes, start dropping them. - // Prefer to keep explicitly mapped axes. - if (mAxes.size() > PointerCoords::MAX_AXES) { - ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", - getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); - pruneAxes(true); - pruneAxes(false); - } - - // Assign generic axis ids to remaining axes. - int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && - haveAxis(nextGenericAxisId)) { - nextGenericAxisId += 1; - } - - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { - axis.axisInfo.axis = nextGenericAxisId; - nextGenericAxisId += 1; - } else { - ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " - "have already been assigned to other axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; - } - } - } - } -} - -bool JoystickInputMapper::haveAxis(int32_t axisId) { - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - if (axis.axisInfo.axis == axisId || - (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { - return true; - } - } - return false; -} - -void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { - size_t i = mAxes.size(); - while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { - if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { - continue; - } - ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i); - } -} - -bool JoystickInputMapper::isCenteredAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_X: - case AMOTION_EVENT_AXIS_Y: - case AMOTION_EVENT_AXIS_Z: - case AMOTION_EVENT_AXIS_RX: - case AMOTION_EVENT_AXIS_RY: - case AMOTION_EVENT_AXIS_RZ: - case AMOTION_EVENT_AXIS_HAT_X: - case AMOTION_EVENT_AXIS_HAT_Y: - case AMOTION_EVENT_AXIS_ORIENTATION: - case AMOTION_EVENT_AXIS_RUDDER: - case AMOTION_EVENT_AXIS_WHEEL: - return true; - default: - return false; - } -} - -void JoystickInputMapper::reset(nsecs_t when) { - // Recenter all axes. - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - axis.resetValue(); - } - - InputMapper::reset(when); -} - -void JoystickInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->code); - if (index >= 0) { - Axis& axis = mAxes.editValueAt(index); - float newValue, highNewValue; - switch (axis.axisInfo.mode) { - case AxisInfo::MODE_INVERT: - newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + - axis.offset; - highNewValue = 0.0f; - break; - case AxisInfo::MODE_SPLIT: - if (rawEvent->value < axis.axisInfo.splitValue) { - newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + - axis.offset; - highNewValue = 0.0f; - } else if (rawEvent->value > axis.axisInfo.splitValue) { - newValue = 0.0f; - highNewValue = - (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + - axis.highOffset; - } else { - newValue = 0.0f; - highNewValue = 0.0f; - } - break; - default: - newValue = rawEvent->value * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - } - axis.newValue = newValue; - axis.highNewValue = highNewValue; - } - break; - } - - case EV_SYN: - switch (rawEvent->code) { - case SYN_REPORT: - sync(rawEvent->when, false /*force*/); - break; - } - break; - } -} - -void JoystickInputMapper::sync(nsecs_t when, bool force) { - if (!filterAxes(force)) { - return; - } - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = 0; - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, - axis.highCurrentValue); - } - } - - // Moving a joystick axis should not wake the device because joysticks can - // be fairly noisy even when not in use. On the other hand, pushing a gamepad - // button will likely wake the device. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); -} - -void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, - float value) { - pointerCoords->setAxisValue(axis, value); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to produce - * values for the old axes as mirrors of the value of their corresponding - * new axes. */ - int32_t compatAxis = getCompatAxis(axis); - if (compatAxis >= 0) { - pointerCoords->setAxisValue(compatAxis, value); - } -} - -bool JoystickInputMapper::filterAxes(bool force) { - bool atLeastOneSignificantChange = force; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (force || - hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, - axis.max)) { - axis.currentValue = axis.newValue; - atLeastOneSignificantChange = true; - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - if (force || - hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, - axis.min, axis.max)) { - axis.highCurrentValue = axis.highNewValue; - atLeastOneSignificantChange = true; - } - } - } - return atLeastOneSignificantChange; -} - -bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, - float currentValue, float min, float max) { - if (newValue != currentValue) { - // Filter out small changes in value unless the value is converging on the axis - // bounds or center point. This is intended to reduce the amount of information - // sent to applications by particularly noisy joysticks (such as PS3). - if (fabs(newValue - currentValue) > filter || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { - return true; - } - } - return false; -} - -bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, - float currentValue, - float thresholdValue) { - float newDistance = fabs(newValue - thresholdValue); - if (newDistance < filter) { - float oldDistance = fabs(currentValue - thresholdValue); - if (newDistance < oldDistance) { - return true; - } - } - return false; -} - } // namespace android diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp index 7c2311266c..a8971411a4 100644 --- a/services/inputflinger/reader/InputReaderFactory.cpp +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -15,6 +15,7 @@ */ #include "InputReaderFactory.h" + #include "InputReader.h" namespace android { diff --git a/services/inputflinger/reader/JoystickInputMapper.cpp b/services/inputflinger/reader/JoystickInputMapper.cpp new file mode 100644 index 0000000000..50adf73c76 --- /dev/null +++ b/services/inputflinger/reader/JoystickInputMapper.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "JoystickInputMapper.h" + +namespace android { + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} + +JoystickInputMapper::~JoystickInputMapper() {} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (size_t i = 0; i < mAxes.size(); i++) { + const Axis& axis = mAxes.valueAt(i); + addMotionRange(axis.axisInfo.axis, axis, info); + + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + addMotionRange(axis.axisInfo.highAxis, axis, info); + } + } +} + +void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { + info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, + axis.resolution); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to register + * the old axes as duplicates of their corresponding new ones. */ + int32_t compatAxis = getCompatAxis(axisId); + if (compatAxis >= 0) { + info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, + axis.fuzz, axis.resolution); + } +} + +/* A mapping from axes the joystick actually has to the axes that should be + * artificially created for compatibility purposes. + * Returns -1 if no compatibility axis is needed. */ +int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_LTRIGGER: + return AMOTION_EVENT_AXIS_BRAKE; + case AMOTION_EVENT_AXIS_RTRIGGER: + return AMOTION_EVENT_AXIS_GAS; + } + return -1; +} + +void JoystickInputMapper::dump(std::string& dump) { + dump += INDENT2 "Joystick Input Mapper:\n"; + + dump += INDENT3 "Axes:\n"; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + const char* label = getAxisLabel(axis.axisInfo.axis); + if (label) { + dump += StringPrintf(INDENT4 "%s", label); + } else { + dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + label = getAxisLabel(axis.axisInfo.highAxis); + if (label) { + dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); + } else { + dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, + axis.axisInfo.splitValue); + } + } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { + dump += " (invert)"; + } + + dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " + "highScale=%0.5f, highOffset=%0.5f\n", + axis.scale, axis.offset, axis.highScale, axis.highOffset); + dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", + mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, + axis.rawAxisInfo.resolution); + } +} + +void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { + continue; // axis must be claimed by a different device + } + + RawAbsoluteAxisInfo rawAxisInfo; + getAbsoluteAxisInfo(abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + // Map axis. + AxisInfo axisInfo; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + } + + // Apply flat override. + int32_t rawFlat = + axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + // Calculate scaling factors and limits. + Axis axis; + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else if (isCenteredAxis(axisInfo.axis)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, + offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; + + mAxes.add(abs, axis); + } + } + + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", + getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axisInfo.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && + haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } + + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axisInfo.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } + } + } + } +} + +bool JoystickInputMapper::haveAxis(int32_t axisId) { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + if (axis.axisInfo.axis == axisId || + (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { + return true; + } + } + return false; +} + +void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { + size_t i = mAxes.size(); + while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { + if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + continue; + } + ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i); + } +} + +bool JoystickInputMapper::isCenteredAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_Z: + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + case AMOTION_EVENT_AXIS_HAT_X: + case AMOTION_EVENT_AXIS_HAT_Y: + case AMOTION_EVENT_AXIS_ORIENTATION: + case AMOTION_EVENT_AXIS_RUDDER: + case AMOTION_EVENT_AXIS_WHEEL: + return true; + default: + return false; + } +} + +void JoystickInputMapper::reset(nsecs_t when) { + // Recenter all axes. + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + axis.resetValue(); + } + + InputMapper::reset(when); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + ssize_t index = mAxes.indexOfKey(rawEvent->code); + if (index >= 0) { + Axis& axis = mAxes.editValueAt(index); + float newValue, highNewValue; + switch (axis.axisInfo.mode) { + case AxisInfo::MODE_INVERT: + newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + break; + case AxisInfo::MODE_SPLIT: + if (rawEvent->value < axis.axisInfo.splitValue) { + newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + } else if (rawEvent->value > axis.axisInfo.splitValue) { + newValue = 0.0f; + highNewValue = + (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + + axis.highOffset; + } else { + newValue = 0.0f; + highNewValue = 0.0f; + } + break; + default: + newValue = rawEvent->value * axis.scale + axis.offset; + highNewValue = 0.0f; + break; + } + axis.newValue = newValue; + axis.highNewValue = highNewValue; + } + break; + } + + case EV_SYN: + switch (rawEvent->code) { + case SYN_REPORT: + sync(rawEvent->when, false /*force*/); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when, bool force) { + if (!filterAxes(force)) { + return; + } + + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = 0; + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, + axis.highCurrentValue); + } + } + + // Moving a joystick axis should not wake the device because joysticks can + // be fairly noisy even when not in use. On the other hand, pushing a gamepad + // button will likely wake the device. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&args); +} + +void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, + float value) { + pointerCoords->setAxisValue(axis, value); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to produce + * values for the old axes as mirrors of the value of their corresponding + * new axes. */ + int32_t compatAxis = getCompatAxis(axis); + if (compatAxis >= 0) { + pointerCoords->setAxisValue(compatAxis, value); + } +} + +bool JoystickInputMapper::filterAxes(bool force) { + bool atLeastOneSignificantChange = force; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (force || + hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, + axis.max)) { + axis.currentValue = axis.newValue; + atLeastOneSignificantChange = true; + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + if (force || + hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, + axis.min, axis.max)) { + axis.highCurrentValue = axis.highNewValue; + atLeastOneSignificantChange = true; + } + } + } + return atLeastOneSignificantChange; +} + +bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, + float currentValue, float min, float max) { + if (newValue != currentValue) { + // Filter out small changes in value unless the value is converging on the axis + // bounds or center point. This is intended to reduce the amount of information + // sent to applications by particularly noisy joysticks (such as PS3). + if (fabs(newValue - currentValue) > filter || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { + return true; + } + } + return false; +} + +bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, + float thresholdValue) { + float newDistance = fabs(newValue - thresholdValue); + if (newDistance < filter) { + float oldDistance = fabs(currentValue - thresholdValue); + if (newDistance < oldDistance) { + return true; + } + } + return false; +} + +} // namespace android diff --git a/services/inputflinger/reader/JoystickInputMapper.h b/services/inputflinger/reader/JoystickInputMapper.h new file mode 100644 index 0000000000..1b071d0480 --- /dev/null +++ b/services/inputflinger/reader/JoystickInputMapper.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H +#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class JoystickInputMapper : public InputMapper { +public: + explicit JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + struct Axis { + RawAbsoluteAxisInfo rawAxisInfo; + AxisInfo axisInfo; + + bool explicitlyMapped; // true if the axis was explicitly assigned an axis id + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + float highScale; // scale factor from raw to normalized values of high split + float highOffset; // offset to add after scaling for normalization of high split + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units/mm + + float filter; // filter out small variations of this size + float currentValue; // current value + float newValue; // most recent value + float highCurrentValue; // current value of high split + float highNewValue; // most recent value of high split + + void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, + bool explicitlyMapped, float scale, float offset, float highScale, + float highOffset, float min, float max, float flat, float fuzz, + float resolution) { + this->rawAxisInfo = rawAxisInfo; + this->axisInfo = axisInfo; + this->explicitlyMapped = explicitlyMapped; + this->scale = scale; + this->offset = offset; + this->highScale = highScale; + this->highOffset = highOffset; + this->min = min; + this->max = max; + this->flat = flat; + this->fuzz = fuzz; + this->resolution = resolution; + this->filter = 0; + resetValue(); + } + + void resetValue() { + this->currentValue = 0; + this->newValue = 0; + this->highCurrentValue = 0; + this->highNewValue = 0; + } + }; + + // Axes indexed by raw ABS_* axis index. + KeyedVector mAxes; + + void sync(nsecs_t when, bool force); + + bool haveAxis(int32_t axisId); + void pruneAxes(bool ignoreExplicitlyMappedAxes); + bool filterAxes(bool force); + + static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, + float min, float max); + static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, float thresholdValue); + + static bool isCenteredAxis(int32_t axis); + static int32_t getCompatAxis(int32_t axis); + + static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); + static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/KeyboardInputMapper.cpp b/services/inputflinger/reader/KeyboardInputMapper.cpp new file mode 100644 index 0000000000..f51d4a0d4a --- /dev/null +++ b/services/inputflinger/reader/KeyboardInputMapper.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "KeyboardInputMapper.h" + +namespace android { + +// --- Static Definitions --- + +static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, + const int32_t map[][4], size_t mapSize) { + if (orientation != DISPLAY_ORIENTATION_0) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][orientation]; + } + } + } + return value; +} + +static const int32_t keyCodeRotationMap[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, +}; + +static const size_t keyCodeRotationMapSize = + sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); + +static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], + size_t mapSize) { + if (orientation == DISPLAY_ORIENTATION_180) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][1]; + } + } + } + return value; +} + +// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X +static int32_t stemKeyRotationMap[][2] = { + // key codes enumerated with the original (unrotated) key first + // no rotation, 180 degree rotation + {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, + {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, + {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, + {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, +}; + +static const size_t stemKeyRotationMapSize = + sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); + +static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); + return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, + keyCodeRotationMapSize); +} + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) + : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} + +KeyboardInputMapper::~KeyboardInputMapper() {} + +uint32_t KeyboardInputMapper::getSources() { + return mSource; +} + +int32_t KeyboardInputMapper::getOrientation() { + if (mViewport) { + return mViewport->orientation; + } + return DISPLAY_ORIENTATION_0; +} + +int32_t KeyboardInputMapper::getDisplayId() { + if (mViewport) { + return mViewport->displayId; + } + return ADISPLAY_ID_NONE; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); +} + +void KeyboardInputMapper::dump(std::string& dump) { + dump += INDENT2 "Keyboard Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); + dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); + dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +std::optional KeyboardInputMapper::findViewport( + nsecs_t when, const InputReaderConfiguration* config) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // No associated display defined, try to find default display if orientationAware. + if (mParameters.orientationAware) { + return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return std::nullopt; +} + +void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mViewport = findViewport(when, config); + } +} + +static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { + int32_t mapped = 0; + if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { + for (size_t i = 0; i < stemKeyRotationMapSize; i++) { + if (stemKeyRotationMap[i][0] == keyCode) { + stemKeyRotationMap[i][1] = mapped; + return; + } + } + } +} + +void KeyboardInputMapper::configureParameters() { + mParameters.orientationAware = false; + const PropertyMap& config = getDevice()->getConfiguration(); + config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); + + if (mParameters.orientationAware) { + mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); + mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); + mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); + mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); + } + + mParameters.handlesKeyRepeat = false; + config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); +} + +void KeyboardInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); + dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); +} + +void KeyboardInputMapper::reset(nsecs_t when) { + mMetaState = AMETA_NONE; + mDownTime = 0; + mKeyDowns.clear(); + mCurrentHidUsage = 0; + + resetLedState(); + + InputMapper::reset(when); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->code; + int32_t usageCode = mCurrentHidUsage; + mCurrentHidUsage = 0; + + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); + } + break; + } + case EV_MSC: { + if (rawEvent->code == MSC_SCAN) { + mCurrentHidUsage = rawEvent->value; + } + break; + } + case EV_SYN: { + if (rawEvent->code == SYN_REPORT) { + mCurrentHidUsage = 0; + } + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE || scanCode >= KEY_OK || + (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || + (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); +} + +bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + } + return false; +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { + int32_t keyCode; + int32_t keyMetaState; + uint32_t policyFlags; + + if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, + &keyMetaState, &policyFlags)) { + keyCode = AKEYCODE_UNKNOWN; + keyMetaState = mMetaState; + policyFlags = 0; + } + + if (down) { + // Rotate key codes according to orientation if needed. + if (mParameters.orientationAware) { + keyCode = rotateKeyCode(keyCode, getOrientation()); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + } else { + // key down + if ((policyFlags & POLICY_FLAG_VIRTUAL) && + mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { + return; + } + if (policyFlags & POLICY_FLAG_GESTURE) { + mDevice->cancelTouch(when); + } + + KeyDown keyDown; + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + mKeyDowns.push_back(keyDown); + } + + mDownTime = when; + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + } else { + // key was not actually down + ALOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().c_str(), keyCode, scanCode); + return; + } + } + + if (updateMetaStateIfNeeded(keyCode, down)) { + // If global meta state changed send it along with the key. + // If it has not changed then we'll use what keymap gave us, + // since key replacement logic might temporarily reset a few + // meta bits for given key. + keyMetaState = mMetaState; + } + + nsecs_t downTime = mDownTime; + + // Key down on external an keyboard should wake the device. + // We don't do this for internal keyboards to prevent them from waking up in your pocket. + // For internal keyboards, the key layout file should specify the policy flags for + // each wake key individually. + // TODO: Use the input device configuration to control this behavior more finely. + if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mParameters.handlesKeyRepeat) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + getListener()->notifyKey(&args); +} + +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} + +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} + +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} + +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + +void KeyboardInputMapper::updateMetaState(int32_t keyCode) { + updateMetaStateIfNeeded(keyCode, false); +} + +bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { + int32_t oldMetaState = mMetaState; + int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); + bool metaStateChanged = oldMetaState != newMetaState; + if (metaStateChanged) { + mMetaState = newMetaState; + updateLedState(false); + + getContext()->updateGlobalMetaState(); + } + + return metaStateChanged; +} + +void KeyboardInputMapper::resetLedState() { + initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); + initializeLedState(mNumLockLedState, ALED_NUM_LOCK); + initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); + + updateLedState(true); +} + +void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { + ledState.avail = getEventHub()->hasLed(getDeviceId(), led); + ledState.on = false; +} + +void KeyboardInputMapper::updateLedState(bool reset) { + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); + updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); + updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); +} + +void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, + int32_t modifier, bool reset) { + if (ledState.avail) { + bool desiredState = (mMetaState & modifier) != 0; + if (reset || ledState.on != desiredState) { + getEventHub()->setLedState(getDeviceId(), led, desiredState); + ledState.on = desiredState; + } + } +} + +std::optional KeyboardInputMapper::getAssociatedDisplayId() { + if (mViewport) { + return std::make_optional(mViewport->displayId); + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h new file mode 100644 index 0000000000..827e31a1bd --- /dev/null +++ b/services/inputflinger/reader/Macros.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_MACROS_H +#define _UI_INPUTREADER_MACROS_H + +#define LOG_TAG "InputReader" + +//#define LOG_NDEBUG 0 + +// Log debug messages for each raw event received from the EventHub. +#define DEBUG_RAW_EVENTS 0 + +// Log debug messages about touch screen filtering hacks. +#define DEBUG_HACKS 0 + +// Log debug messages about virtual key processing. +#define DEBUG_VIRTUAL_KEYS 0 + +// Log debug messages about pointers. +#define DEBUG_POINTERS 0 + +// Log debug messages about pointer assignment calculations. +#define DEBUG_POINTER_ASSIGNMENT 0 + +// Log debug messages about gesture detection. +#define DEBUG_GESTURES 0 + +// Log debug messages about the vibrator. +#define DEBUG_VIBRATOR 0 + +// Log debug messages about fusing stylus data. +#define DEBUG_STYLUS_FUSION 0 + +#define INDENT " " +#define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " +#define INDENT5 " " + +#include + +namespace android { + +// --- Static Functions --- + +template +inline static T abs(const T& value) { + return value < 0 ? -value : value; +} + +template +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +inline static float avg(float x, float y) { + return (x + y) / 2; +} + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0; +} + +} // namespace android + +#endif // _UI_INPUTREADER_MACROS_H \ No newline at end of file diff --git a/services/inputflinger/reader/MultiTouchInputMapper.cpp b/services/inputflinger/reader/MultiTouchInputMapper.cpp new file mode 100644 index 0000000000..c567c8bf80 --- /dev/null +++ b/services/inputflinger/reader/MultiTouchInputMapper.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "MultiTouchInputMapper.h" + +namespace android { + +// --- Constants --- + +// Maximum number of slots supported when using the slot-based Multitouch Protocol B. +static constexpr size_t MAX_SLOTS = 32; + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), + mSlots(nullptr), + mSlotCount(0), + mUsingSlotsProtocol(false), + mHaveStylus(false) {} + +MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { + delete[] mSlots; +} + +void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, + bool usingSlotsProtocol) { + mSlotCount = slotCount; + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + + delete[] mSlots; + mSlots = new Slot[slotCount]; +} + +void MultiTouchMotionAccumulator::reset(InputDevice* device) { + // Unfortunately there is no way to read the initial contents of the slots. + // So when we reset the accumulator, we must assume they are all zeroes. + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot + // before we start reading events from the device. It is possible that the + // current slot index will not be the same as it was when the first event was + // written into the evdev buffer, which means the input mapper could start + // out of sync with the initial state of the events in the evdev buffer. + // In the extremely unlikely case that this happens, the data from + // two slots will be confused until the next ABS_MT_SLOT event is received. + // This can cause the touch point to "jump", but at least there will be + // no stuck touches. + int32_t initialSlot; + status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, + &initialSlot); + if (status) { + ALOGD("Could not retrieve current multitouch slot index. status=%d", status); + initialSlot = -1; + } + clearSlots(initialSlot); + } else { + clearSlots(-1); + } +} + +void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { + if (mSlots) { + for (size_t i = 0; i < mSlotCount; i++) { + mSlots[i].clear(); + } + } + mCurrentSlot = initialSlot; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { +#if DEBUG_POINTERS + if (newSlot) { + ALOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } +#endif + } else { + Slot* slot = &mSlots[mCurrentSlot]; + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot->mInUse = true; + slot->mAbsMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + slot->mInUse = true; + slot->mAbsMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + slot->mInUse = true; + slot->mAbsMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot->mInUse = true; + slot->mAbsMTTouchMinor = rawEvent->value; + slot->mHaveAbsMTTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot->mInUse = true; + slot->mAbsMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot->mInUse = true; + slot->mAbsMTWidthMinor = rawEvent->value; + slot->mHaveAbsMTWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot->mInUse = true; + slot->mAbsMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot->mInUse = false; + } else { + slot->mInUse = true; + slot->mAbsMTTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot->mInUse = true; + slot->mAbsMTPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot->mInUse = true; + slot->mAbsMTDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->mInUse = true; + slot->mAbsMTToolType = rawEvent->value; + slot->mHaveAbsMTToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + clearSlots(-1); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +// --- MultiTouchMotionAccumulator::Slot --- + +MultiTouchMotionAccumulator::Slot::Slot() { + clear(); +} + +void MultiTouchMotionAccumulator::Slot::clear() { + mInUse = false; + mHaveAbsMTTouchMinor = false; + mHaveAbsMTWidthMinor = false; + mHaveAbsMTToolType = false; + mAbsMTPositionX = 0; + mAbsMTPositionY = 0; + mAbsMTTouchMajor = 0; + mAbsMTTouchMinor = 0; + mAbsMTWidthMajor = 0; + mAbsMTWidthMinor = 0; + mAbsMTOrientation = 0; + mAbsMTTrackingId = -1; + mAbsMTPressure = 0; + mAbsMTDistance = 0; + mAbsMTToolType = 0; +} + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMTToolType) { + switch (mAbsMTToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +MultiTouchInputMapper::~MultiTouchInputMapper() {} + +void MultiTouchInputMapper::reset(nsecs_t when) { + mMultiTouchMotionAccumulator.reset(getDevice()); + + mPointerIdBits.clear(); + + TouchInputMapper::reset(when); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mMultiTouchMotionAccumulator.process(rawEvent); +} + +void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); + size_t outCount = 0; + BitSet32 newPointerIdBits; + mHavePointerIds = true; + + for (size_t inIndex = 0; inIndex < inCount; inIndex++) { + const MultiTouchMotionAccumulator::Slot* inSlot = + mMultiTouchMotionAccumulator.getSlot(inIndex); + if (!inSlot->isInUse()) { + continue; + } + + if (outCount >= MAX_POINTERS) { +#if DEBUG_POINTERS + ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().c_str(), MAX_POINTERS); +#endif + break; // too many fingers! + } + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; + outPointer.x = inSlot->getX(); + outPointer.y = inSlot->getY(); + outPointer.pressure = inSlot->getPressure(); + outPointer.touchMajor = inSlot->getTouchMajor(); + outPointer.touchMinor = inSlot->getTouchMinor(); + outPointer.toolMajor = inSlot->getToolMajor(); + outPointer.toolMinor = inSlot->getToolMinor(); + outPointer.orientation = inSlot->getOrientation(); + outPointer.distance = inSlot->getDistance(); + outPointer.tiltX = 0; + outPointer.tiltY = 0; + + outPointer.toolType = inSlot->getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + } + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); + outPointer.isHovering = isHovering; + + // Assign pointer id using tracking id if available. + if (mHavePointerIds) { + int32_t trackingId = inSlot->getTrackingId(); + int32_t id = -1; + if (trackingId >= 0) { + for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { + uint32_t n = idBits.clearFirstMarkedBit(); + if (mPointerTrackingIdMap[n] == trackingId) { + id = n; + } + } + + if (id < 0 && !mPointerIdBits.isFull()) { + id = mPointerIdBits.markFirstUnmarkedBit(); + mPointerTrackingIdMap[id] = trackingId; + } + } + if (id < 0) { + mHavePointerIds = false; + outState->rawPointerData.clearIdBits(); + newPointerIdBits.clear(); + } else { + outPointer.id = id; + outState->rawPointerData.idToIndex[id] = outCount; + outState->rawPointerData.markIdBit(id, isHovering); + newPointerIdBits.markBit(id); + } + } + outCount += 1; + } + + outState->rawPointerData.pointerCount = outCount; + mPointerIdBits = newPointerIdBits; + + mMultiTouchMotionAccumulator.finishSync(); +} + +void MultiTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); + getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); + getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); + getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); + + if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && + mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { + size_t slotCount = mRawPointerAxes.slot.maxValue + 1; + if (slotCount > MAX_SLOTS) { + ALOGW("MultiTouch Device %s reported %zu slots but the framework " + "only supports a maximum of %zu slots at this time.", + getDeviceName().c_str(), slotCount, MAX_SLOTS); + slotCount = MAX_SLOTS; + } + mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); + } else { + mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, + false /*usingSlotsProtocol*/); + } +} + +bool MultiTouchInputMapper::hasStylus() const { + return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/RotaryEncoderInputMapper.cpp new file mode 100644 index 0000000000..e113ccad84 --- /dev/null +++ b/services/inputflinger/reader/RotaryEncoderInputMapper.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "RotaryEncoderInputMapper.h" + +#include "CursorScrollAccumulator.h" + +namespace android { + +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) + : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { + mSource = AINPUT_SOURCE_ROTARY_ENCODER; +} + +RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} + +uint32_t RotaryEncoderInputMapper::getSources() { + return mSource; +} + +void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { + float res = 0.0f; + if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { + ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); + } + if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), + mScalingFactor)) { + ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," + "default to 1.0!\n"); + mScalingFactor = 1.0f; + } + info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + res * mScalingFactor); + } +} + +void RotaryEncoderInputMapper::dump(std::string& dump) { + dump += INDENT2 "Rotary Encoder Input Mapper:\n"; + dump += StringPrintf(INDENT3 "HaveWheel: %s\n", + toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); +} + +void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + if (!changes) { + mRotaryEncoderScrollAccumulator.configure(getDevice()); + } + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } else { + mOrientation = DISPLAY_ORIENTATION_0; + } + } +} + +void RotaryEncoderInputMapper::reset(nsecs_t when) { + mRotaryEncoderScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + mRotaryEncoderScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void RotaryEncoderInputMapper::sync(nsecs_t when) { + PointerCoords pointerCoords; + pointerCoords.clear(); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + bool scrolled = scroll != 0; + + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + // Moving the rotary encoder should wake the device (if specified). + uint32_t policyFlags = 0; + if (scrolled && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + + // Send motion event. + if (scrolled) { + int32_t metaState = mContext->getGlobalMetaState(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + + mRotaryEncoderScrollAccumulator.finishSync(); +} + +} // namespace android diff --git a/services/inputflinger/reader/RotaryEncoderInputMapper.h b/services/inputflinger/reader/RotaryEncoderInputMapper.h new file mode 100644 index 0000000000..26488373bd --- /dev/null +++ b/services/inputflinger/reader/RotaryEncoderInputMapper.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H +#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H + +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +namespace android { + +class RotaryEncoderInputMapper : public InputMapper { +public: + explicit RotaryEncoderInputMapper(InputDevice* device); + virtual ~RotaryEncoderInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + CursorScrollAccumulator mRotaryEncoderScrollAccumulator; + + int32_t mSource; + float mScalingFactor; + int32_t mOrientation; + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/SingleTouchInputMapper.cpp b/services/inputflinger/reader/SingleTouchInputMapper.cpp new file mode 100644 index 0000000000..440d282686 --- /dev/null +++ b/services/inputflinger/reader/SingleTouchInputMapper.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchInputMapper.h" + +namespace android { + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +SingleTouchInputMapper::~SingleTouchInputMapper() {} + +void SingleTouchInputMapper::reset(nsecs_t when) { + mSingleTouchMotionAccumulator.reset(getDevice()); + + TouchInputMapper::reset(when); +} + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mSingleTouchMotionAccumulator.process(rawEvent); +} + +void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + if (mTouchButtonAccumulator.isToolActive()) { + outState->rawPointerData.pointerCount = 1; + outState->rawPointerData.idToIndex[0] = 0; + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && + mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); + outState->rawPointerData.markIdBit(0, isHovering); + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; + outPointer.id = 0; + outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); + outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); + outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + outPointer.touchMajor = 0; + outPointer.touchMinor = 0; + outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.orientation = 0; + outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); + outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); + outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + outPointer.isHovering = isHovering; + } +} + +void SingleTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); + getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); +} + +bool SingleTouchInputMapper::hasStylus() const { + return mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..e9ba727a0d --- /dev/null +++ b/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchMotionAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { + clearAbsoluteAxes(); +} + +void SingleTouchMotionAccumulator::reset(InputDevice* device) { + mAbsX = device->getAbsoluteAxisValue(ABS_X); + mAbsY = device->getAbsoluteAxisValue(ABS_Y); + mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); + mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); + mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); + mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); + mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); +} + +void SingleTouchMotionAccumulator::clearAbsoluteAxes() { + mAbsX = 0; + mAbsY = 0; + mAbsPressure = 0; + mAbsToolWidth = 0; + mAbsDistance = 0; + mAbsTiltX = 0; + mAbsTiltY = 0; +} + +void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + switch (rawEvent->code) { + case ABS_X: + mAbsX = rawEvent->value; + break; + case ABS_Y: + mAbsY = rawEvent->value; + break; + case ABS_PRESSURE: + mAbsPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAbsToolWidth = rawEvent->value; + break; + case ABS_DISTANCE: + mAbsDistance = rawEvent->value; + break; + case ABS_TILT_X: + mAbsTiltX = rawEvent->value; + break; + case ABS_TILT_Y: + mAbsTiltY = rawEvent->value; + break; + } + } +} + +} // namespace android diff --git a/services/inputflinger/reader/SwitchInputMapper.cpp b/services/inputflinger/reader/SwitchInputMapper.cpp new file mode 100644 index 0000000000..4ff941f5cf --- /dev/null +++ b/services/inputflinger/reader/SwitchInputMapper.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "SwitchInputMapper.h" + +namespace android { + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) + : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} + +SwitchInputMapper::~SwitchInputMapper() {} + +uint32_t SwitchInputMapper::getSources() { + return AINPUT_SOURCE_SWITCH; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->code, rawEvent->value); + break; + + case EV_SYN: + if (rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } + } +} + +void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { + if (switchCode >= 0 && switchCode < 32) { + if (switchValue) { + mSwitchValues |= 1 << switchCode; + } else { + mSwitchValues &= ~(1 << switchCode); + } + mUpdatedSwitchMask |= 1 << switchCode; + } +} + +void SwitchInputMapper::sync(nsecs_t when) { + if (mUpdatedSwitchMask) { + uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; + NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, + mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + + mUpdatedSwitchMask = 0; + } +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + +void SwitchInputMapper::dump(std::string& dump) { + dump += INDENT2 "Switch Input Mapper:\n"; + dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); +} + +} // namespace android diff --git a/services/inputflinger/reader/TouchButtonAccumulator.cpp b/services/inputflinger/reader/TouchButtonAccumulator.cpp new file mode 100644 index 0000000000..d2f06c86fd --- /dev/null +++ b/services/inputflinger/reader/TouchButtonAccumulator.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TouchButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { + clearButtons(); +} + +void TouchButtonAccumulator::configure(InputDevice* device) { + mHaveBtnTouch = device->hasKey(BTN_TOUCH); + mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || + device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || + device->hasKey(BTN_TOOL_AIRBRUSH); +} + +void TouchButtonAccumulator::reset(InputDevice* device) { + mBtnTouch = device->isKeyPressed(BTN_TOUCH); + mBtnStylus = device->isKeyPressed(BTN_STYLUS); + // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); + mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); +} + +void TouchButtonAccumulator::clearButtons() { + mBtnTouch = 0; + mBtnStylus = 0; + mBtnStylus2 = 0; + mBtnToolFinger = 0; + mBtnToolPen = 0; + mBtnToolRubber = 0; + mBtnToolBrush = 0; + mBtnToolPencil = 0; + mBtnToolAirbrush = 0; + mBtnToolMouse = 0; + mBtnToolLens = 0; + mBtnToolDoubleTap = 0; + mBtnToolTripleTap = 0; + mBtnToolQuadTap = 0; +} + +void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_TOUCH: + mBtnTouch = rawEvent->value; + break; + case BTN_STYLUS: + mBtnStylus = rawEvent->value; + break; + case BTN_STYLUS2: + case BTN_0: // BTN_0 is what gets mapped for the HID usage + // Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = rawEvent->value; + break; + case BTN_TOOL_FINGER: + mBtnToolFinger = rawEvent->value; + break; + case BTN_TOOL_PEN: + mBtnToolPen = rawEvent->value; + break; + case BTN_TOOL_RUBBER: + mBtnToolRubber = rawEvent->value; + break; + case BTN_TOOL_BRUSH: + mBtnToolBrush = rawEvent->value; + break; + case BTN_TOOL_PENCIL: + mBtnToolPencil = rawEvent->value; + break; + case BTN_TOOL_AIRBRUSH: + mBtnToolAirbrush = rawEvent->value; + break; + case BTN_TOOL_MOUSE: + mBtnToolMouse = rawEvent->value; + break; + case BTN_TOOL_LENS: + mBtnToolLens = rawEvent->value; + break; + case BTN_TOOL_DOUBLETAP: + mBtnToolDoubleTap = rawEvent->value; + break; + case BTN_TOOL_TRIPLETAP: + mBtnToolTripleTap = rawEvent->value; + break; + case BTN_TOOL_QUADTAP: + mBtnToolQuadTap = rawEvent->value; + break; + } + } +} + +uint32_t TouchButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnStylus) { + result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + } + if (mBtnStylus2) { + result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } + return result; +} + +int32_t TouchButtonAccumulator::getToolType() const { + if (mBtnToolMouse || mBtnToolLens) { + return AMOTION_EVENT_TOOL_TYPE_MOUSE; + } + if (mBtnToolRubber) { + return AMOTION_EVENT_TOOL_TYPE_ERASER; + } + if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { + return AMOTION_EVENT_TOOL_TYPE_FINGER; + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +bool TouchButtonAccumulator::isToolActive() const { + return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || + mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || + mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; +} + +bool TouchButtonAccumulator::isHovering() const { + return mHaveBtnTouch && !mBtnTouch; +} + +bool TouchButtonAccumulator::hasStylus() const { + return mHaveStylus; +} + +} // namespace android diff --git a/services/inputflinger/reader/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/TouchCursorInputMapperCommon.h new file mode 100644 index 0000000000..efa3d6d2b2 --- /dev/null +++ b/services/inputflinger/reader/TouchCursorInputMapperCommon.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderContext.h" + +#include + +namespace android { + +// --- Static Definitions --- + +static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { + float temp; + switch (orientation) { + case DISPLAY_ORIENTATION_90: + temp = *deltaX; + *deltaX = *deltaY; + *deltaY = -temp; + break; + + case DISPLAY_ORIENTATION_180: + *deltaX = -*deltaX; + *deltaY = -*deltaY; + break; + + case DISPLAY_ORIENTATION_270: + temp = *deltaX; + *deltaX = -*deltaY; + *deltaY = temp; + break; + } +} + +// Returns true if the pointer should be reported as being down given the specified +// button states. This determines whether the event is reported as a touch event. +static bool isPointerDown(int32_t buttonState) { + return buttonState & + (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY); +} + +static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { + if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && + (currentButtonState & buttonState)) || + (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && + !(currentButtonState & buttonState))) { + NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); + context->getListener()->notifyKey(&args); + } +} + +static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState) { + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, + AKEYCODE_BACK); + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD); +} + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H diff --git a/services/inputflinger/reader/TouchInputMapper.cpp b/services/inputflinger/reader/TouchInputMapper.cpp new file mode 100644 index 0000000000..6bd0ea959c --- /dev/null +++ b/services/inputflinger/reader/TouchInputMapper.cpp @@ -0,0 +1,3883 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "TouchInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchButtonAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +namespace android { + +// --- Constants --- + +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + +// Artificial latency on synthetic events created from stylus data without corresponding touch +// data. +static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); + +// --- Static Definitions --- + +template +inline static void swap(T& a, T& b) { + T temp = a; + a = b; + b = temp; +} + +static float calculateCommonVector(float a, float b) { + if (a > 0 && b > 0) { + return a < b ? a : b; + } else if (a < 0 && b < 0) { + return a > b ? a : b; + } else { + return 0; + } +} + +inline static float distance(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); +} + +inline static int32_t signExtendNybble(int32_t value) { + return value >= 8 ? value - 16 : value; +} + +// --- RawPointerAxes --- + +RawPointerAxes::RawPointerAxes() { + clear(); +} + +void RawPointerAxes::clear() { + x.clear(); + y.clear(); + pressure.clear(); + touchMajor.clear(); + touchMinor.clear(); + toolMajor.clear(); + toolMinor.clear(); + orientation.clear(); + distance.clear(); + tiltX.clear(); + tiltY.clear(); + trackingId.clear(); + slot.clear(); +} + +// --- RawPointerData --- + +RawPointerData::RawPointerData() { + clear(); +} + +void RawPointerData::clear() { + pointerCount = 0; + clearIdBits(); +} + +void RawPointerData::copyFrom(const RawPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + + int id = pointers[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { + float x = 0, y = 0; + uint32_t count = touchingIdBits.count(); + if (count) { + for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const Pointer& pointer = pointerForId(id); + x += pointer.x; + y += pointer.y; + } + x /= count; + y /= count; + } + *outX = x; + *outY = y; +} + +// --- CookedPointerData --- + +CookedPointerData::CookedPointerData() { + clear(); +} + +void CookedPointerData::clear() { + pointerCount = 0; + hoveringIdBits.clear(); + touchingIdBits.clear(); +} + +void CookedPointerData::copyFrom(const CookedPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(other.pointerProperties[i]); + pointerCoords[i].copyFrom(other.pointerCoords[i]); + + int id = pointerProperties[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device) + : InputMapper(device), + mSource(0), + mDeviceMode(DEVICE_MODE_DISABLED), + mSurfaceWidth(-1), + mSurfaceHeight(-1), + mSurfaceLeft(0), + mSurfaceTop(0), + mPhysicalWidth(-1), + mPhysicalHeight(-1), + mPhysicalLeft(0), + mPhysicalTop(0), + mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} + +TouchInputMapper::~TouchInputMapper() {} + +uint32_t TouchInputMapper::getSources() { + return mSource; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mDeviceMode != DEVICE_MODE_DISABLED) { + info->addMotionRange(mOrientedRanges.x); + info->addMotionRange(mOrientedRanges.y); + info->addMotionRange(mOrientedRanges.pressure); + + if (mOrientedRanges.haveSize) { + info->addMotionRange(mOrientedRanges.size); + } + + if (mOrientedRanges.haveTouchSize) { + info->addMotionRange(mOrientedRanges.touchMajor); + info->addMotionRange(mOrientedRanges.touchMinor); + } + + if (mOrientedRanges.haveToolSize) { + info->addMotionRange(mOrientedRanges.toolMajor); + info->addMotionRange(mOrientedRanges.toolMinor); + } + + if (mOrientedRanges.haveOrientation) { + info->addMotionRange(mOrientedRanges.orientation); + } + + if (mOrientedRanges.haveDistance) { + info->addMotionRange(mOrientedRanges.distance); + } + + if (mOrientedRanges.haveTilt) { + info->addMotionRange(mOrientedRanges.tilt); + } + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + } + info->setButtonUnderPad(mParameters.hasButtonUnderPad); + } +} + +void TouchInputMapper::dump(std::string& dump) { + dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); + dumpParameters(dump); + dumpVirtualKeys(dump); + dumpRawPointerAxes(dump); + dumpCalibration(dump); + dumpAffineTransformation(dump); + dumpSurface(dump); + + dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); + dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); + dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); + dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); + dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); + dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); + dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); + dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); + dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); + dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); + dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); + dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); + dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); + + dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); + dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", + mLastRawState.rawPointerData.pointerCount); + for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { + const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " + "toolType=%d, isHovering=%s\n", + i, pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, + pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, + pointer.distance, pointer.toolType, toString(pointer.isHovering)); + } + + dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", + mLastCookedState.buttonState); + dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", + mLastCookedState.cookedPointerData.pointerCount); + for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { + const PointerProperties& pointerProperties = + mLastCookedState.cookedPointerData.pointerProperties[i]; + const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " + "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " + "toolMinor=%0.3f, " + "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " + "toolType=%d, isHovering=%s\n", + i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), + pointerProperties.toolType, + toString(mLastCookedState.cookedPointerData.isHovering(i))); + } + + dump += INDENT3 "Stylus Fusion:\n"; + dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", + toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", + mExternalStylusFusionTimeout); + dump += INDENT3 "External Stylus State:\n"; + dumpStylusState(dump, mExternalStylusState); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); + dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); + dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); + dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); + dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); + dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); + } +} + +const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { + switch (deviceMode) { + case DEVICE_MODE_DISABLED: + return "disabled"; + case DEVICE_MODE_DIRECT: + return "direct"; + case DEVICE_MODE_UNSCALED: + return "unscaled"; + case DEVICE_MODE_NAVIGATION: + return "navigation"; + case DEVICE_MODE_POINTER: + return "pointer"; + } + return "unknown"; +} + +void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + mConfig = *config; + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + + // Configure common accumulators. + mCursorScrollAccumulator.configure(getDevice()); + mTouchButtonAccumulator.configure(getDevice()); + + // Configure absolute axis information. + configureRawPointerAxes(); + + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { + // Update location calibration to reflect current settings + updateAffineTransformation(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + // Update pointer speed. + mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + } + + bool resetNeeded = false; + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES | + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + // Configure device sources, surface dimensions, orientation and + // scaling factors. + configureSurface(when, &resetNeeded); + } + + if (changes && resetNeeded) { + // Send reset, unless this is the first time the device has been configured, + // in which case the reader will call reset itself after all mappers are ready. + getDevice()->notifyReset(when); + } +} + +void TouchInputMapper::resolveExternalStylusPresence() { + std::vector devices; + mContext->getExternalStylusDevices(devices); + mExternalStylusConnected = !devices.empty(); + + if (!mExternalStylusConnected) { + resetExternalStylus(); + } +} + +void TouchInputMapper::configureParameters() { + // Use the pointer presentation mode for devices that do not support distinct + // multitouch. The spot-based presentation relies on being able to accurately + // locate two or more fingers on the touch pad. + mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) + ? Parameters::GESTURE_MODE_SINGLE_TOUCH + : Parameters::GESTURE_MODE_MULTI_TOUCH; + + String8 gestureModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), + gestureModeString)) { + if (gestureModeString == "single-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; + } else if (gestureModeString == "multi-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; + } else if (gestureModeString != "default") { + ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); + } + } + + if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + // The device is a touch screen. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + // The device is a pointing device like a track pad. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || + getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is a touch pad of unknown purpose. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + + mParameters.hasButtonUnderPad = + getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); + + String8 deviceTypeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { + if (deviceTypeString == "touchScreen") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (deviceTypeString == "touchPad") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "touchNavigation") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (deviceTypeString != "default") { + ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + } + } + + mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + mParameters.associatedDisplayIsExternal = false; + if (mParameters.orientationAware || + mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || + mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.hasAssociatedDisplay = true; + if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { + mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + String8 uniqueDisplayId; + getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), + uniqueDisplayId); + mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); + } + } + if (getDevice()->getAssociatedDisplayPort()) { + mParameters.hasAssociatedDisplay = true; + } + + // Initial downs on external touch devices should wake the device. + // Normally we don't do this for internal touch screens to prevent them from waking + // up in your pocket but you can enable it using the input device configuration. + mParameters.wake = getDevice()->isExternal(); + getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); +} + +void TouchInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + + switch (mParameters.gestureMode) { + case Parameters::GESTURE_MODE_SINGLE_TOUCH: + dump += INDENT4 "GestureMode: single-touch\n"; + break; + case Parameters::GESTURE_MODE_MULTI_TOUCH: + dump += INDENT4 "GestureMode: multi-touch\n"; + break; + default: + assert(false); + } + + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + dump += INDENT4 "DeviceType: touchScreen\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + dump += INDENT4 "DeviceType: touchPad\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: + dump += INDENT4 "DeviceType: touchNavigation\n"; + break; + case Parameters::DEVICE_TYPE_POINTER: + dump += INDENT4 "DeviceType: pointer\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " + "displayId='%s'\n", + toString(mParameters.hasAssociatedDisplay), + toString(mParameters.associatedDisplayIsExternal), + mParameters.uniqueDisplayId.c_str()); + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void TouchInputMapper::configureRawPointerAxes() { + mRawPointerAxes.clear(); +} + +void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { + dump += INDENT3 "Raw Touch Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); +} + +bool TouchInputMapper::hasExternalStylus() const { + return mExternalStylusConnected; +} + +/** + * Determine which DisplayViewport to use. + * 1. If display port is specified, return the matching viewport. If matching viewport not + * found, then return. + * 2. If a device has associated display, get the matching viewport by either unique id or by + * the display type (internal or external). + * 3. Otherwise, use a non-display viewport. + */ +std::optional TouchInputMapper::findViewport() { + if (mParameters.hasAssociatedDisplay) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // Check if uniqueDisplayId is specified in idc file. + if (!mParameters.uniqueDisplayId.empty()) { + return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); + } + + ViewportType viewportTypeToUse; + if (mParameters.associatedDisplayIsExternal) { + viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; + } else { + viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; + } + + std::optional viewport = + mConfig.getDisplayViewportByType(viewportTypeToUse); + if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { + ALOGW("Input device %s should be associated with external display, " + "fallback to internal one for the external viewport is not found.", + getDeviceName().c_str()); + viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return viewport; + } + + // No associated display, return a non-display viewport. + DisplayViewport newViewport; + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + newViewport.setNonDisplayViewport(rawWidth, rawHeight); + return std::make_optional(newViewport); +} + +void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { + int32_t oldDeviceMode = mDeviceMode; + + resolveExternalStylusPresence(); + + // Determine device mode. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && + mConfig.pointerGesturesEnabled) { + mSource = AINPUT_SOURCE_MOUSE; + mDeviceMode = DEVICE_MODE_POINTER; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && + mParameters.hasAssociatedDisplay) { + mSource = AINPUT_SOURCE_TOUCHSCREEN; + mDeviceMode = DEVICE_MODE_DIRECT; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + if (hasExternalStylus()) { + mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mDeviceMode = DEVICE_MODE_NAVIGATION; + } else { + mSource = AINPUT_SOURCE_TOUCHPAD; + mDeviceMode = DEVICE_MODE_UNSCALED; + } + + // Ensure we have valid X and Y axes. + if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { + ALOGW("Touch device '%s' did not report support for X or Y axis! " + "The device will be inoperable.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Get associated display dimensions. + std::optional newViewport = findViewport(); + if (!newViewport) { + ALOGI("Touch device '%s' could not query the properties of its associated " + "display. The device will be inoperable until the display size " + "becomes available.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + + bool viewportChanged = mViewport != *newViewport; + if (viewportChanged) { + mViewport = *newViewport; + + if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { + // Convert rotated viewport to natural surface coordinates. + int32_t naturalLogicalWidth, naturalLogicalHeight; + int32_t naturalPhysicalWidth, naturalPhysicalHeight; + int32_t naturalPhysicalLeft, naturalPhysicalTop; + int32_t naturalDeviceWidth, naturalDeviceHeight; + switch (mViewport.orientation) { + case DISPLAY_ORIENTATION_90: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalTop = mViewport.physicalLeft; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_180: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + case DISPLAY_ORIENTATION_270: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.physicalTop; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_0: + default: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.physicalLeft; + naturalPhysicalTop = mViewport.physicalTop; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + } + + if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { + ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); + naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; + naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; + } + + mPhysicalWidth = naturalPhysicalWidth; + mPhysicalHeight = naturalPhysicalHeight; + mPhysicalLeft = naturalPhysicalLeft; + mPhysicalTop = naturalPhysicalTop; + + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + + mSurfaceOrientation = + mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; + } else { + mPhysicalWidth = rawWidth; + mPhysicalHeight = rawHeight; + mPhysicalLeft = 0; + mPhysicalTop = 0; + + mSurfaceWidth = rawWidth; + mSurfaceHeight = rawHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceOrientation = DISPLAY_ORIENTATION_0; + } + } + + // If moving between pointer modes, need to reset some state. + bool deviceModeChanged = mDeviceMode != oldDeviceMode; + if (deviceModeChanged) { + mOrientedRanges.clear(); + } + + // Create or update pointer controller if needed. + if (mDeviceMode == DEVICE_MODE_POINTER || + (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { + if (mPointerController == nullptr || viewportChanged) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + } else { + mPointerController.clear(); + } + + if (viewportChanged || deviceModeChanged) { + ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + "display id %d", + getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, + mSurfaceOrientation, mDeviceMode, mViewport.displayId); + + // Configure X and Y factors. + mXScale = float(mSurfaceWidth) / rawWidth; + mYScale = float(mSurfaceHeight) / rawHeight; + mXTranslate = -mSurfaceLeft; + mYTranslate = -mSurfaceTop; + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; + mOrientedRanges.x.source = mSource; + mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; + mOrientedRanges.y.source = mSource; + + configureVirtualKeys(); + + // Scale factor for terms that are not oriented in a particular axis. + // If the pixels are square then xScale == yScale otherwise we fake it + // by choosing an average. + mGeometricScale = avg(mXScale, mYScale); + + // Size of diagonal axis. + float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Size factors. + if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; + } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; + } else { + mSizeScale = 0.0f; + } + + mOrientedRanges.haveTouchSize = true; + mOrientedRanges.haveToolSize = true; + mOrientedRanges.haveSize = true; + + mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; + mOrientedRanges.touchMajor.source = mSource; + mOrientedRanges.touchMajor.min = 0; + mOrientedRanges.touchMajor.max = diagonalSize; + mOrientedRanges.touchMajor.flat = 0; + mOrientedRanges.touchMajor.fuzz = 0; + mOrientedRanges.touchMajor.resolution = 0; + + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; + + mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; + mOrientedRanges.toolMajor.source = mSource; + mOrientedRanges.toolMajor.min = 0; + mOrientedRanges.toolMajor.max = diagonalSize; + mOrientedRanges.toolMajor.flat = 0; + mOrientedRanges.toolMajor.fuzz = 0; + mOrientedRanges.toolMajor.resolution = 0; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; + mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; + + mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; + mOrientedRanges.size.source = mSource; + mOrientedRanges.size.min = 0; + mOrientedRanges.size.max = 1.0; + mOrientedRanges.size.flat = 0; + mOrientedRanges.size.fuzz = 0; + mOrientedRanges.size.resolution = 0; + } else { + mSizeScale = 0.0f; + } + + // Pressure factors. + mPressureScale = 0; + float pressureMax = 1.0; + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || + mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { + if (mCalibration.havePressureScale) { + mPressureScale = mCalibration.pressureScale; + pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; + } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { + mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; + } + } + + mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; + mOrientedRanges.pressure.source = mSource; + mOrientedRanges.pressure.min = 0; + mOrientedRanges.pressure.max = pressureMax; + mOrientedRanges.pressure.flat = 0; + mOrientedRanges.pressure.fuzz = 0; + mOrientedRanges.pressure.resolution = 0; + + // Tilt + mTiltXCenter = 0; + mTiltXScale = 0; + mTiltYCenter = 0; + mTiltYScale = 0; + mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; + if (mHaveTilt) { + mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); + mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); + mTiltXScale = M_PI / 180; + mTiltYScale = M_PI / 180; + + mOrientedRanges.haveTilt = true; + + mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; + mOrientedRanges.tilt.source = mSource; + mOrientedRanges.tilt.min = 0; + mOrientedRanges.tilt.max = M_PI_2; + mOrientedRanges.tilt.flat = 0; + mOrientedRanges.tilt.fuzz = 0; + mOrientedRanges.tilt.resolution = 0; + } + + // Orientation + mOrientationScale = 0; + if (mHaveTilt) { + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI; + mOrientedRanges.orientation.max = M_PI; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } else if (mCalibration.orientationCalibration != + Calibration::ORIENTATION_CALIBRATION_NONE) { + if (mCalibration.orientationCalibration == + Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { + if (mRawPointerAxes.orientation.valid) { + if (mRawPointerAxes.orientation.maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; + } else if (mRawPointerAxes.orientation.minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + } else { + mOrientationScale = 0; + } + } + } + + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } + + // Distance + mDistanceScale = 0; + if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { + if (mCalibration.haveDistanceScale) { + mDistanceScale = mCalibration.distanceScale; + } else { + mDistanceScale = 1.0f; + } + } + + mOrientedRanges.haveDistance = true; + + mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; + mOrientedRanges.distance.source = mSource; + mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; + mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; + mOrientedRanges.distance.flat = 0; + mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; + mOrientedRanges.distance.resolution = 0; + } + + // Compute oriented precision, scales and ranges. + // Note that the maximum value reported is an inclusive maximum value so it is one + // unit less than the total width or height of surface. + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + + mOrientedRanges.x.min = mYTranslate; + mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; + + mOrientedRanges.y.min = mXTranslate; + mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; + break; + + default: + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + + mOrientedRanges.x.min = mXTranslate; + mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; + + mOrientedRanges.y.min = mYTranslate; + mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; + break; + } + + // Location + updateAffineTransformation(); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + // Compute pointer gesture detection parameters. + float rawDiagonal = hypotf(rawWidth, rawHeight); + float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Scale movements such that one whole swipe of the touch pad covers a + // given area relative to the diagonal size of the display when no acceleration + // is applied. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + mPointerXMovementScale = + mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYMovementScale = mPointerXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mPointerXZoomScale = + mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYZoomScale = mPointerXZoomScale; + + // Max width between pointers to detect a swipe gesture is more than some fraction + // of the diagonal axis of the touch pad. Touches that are wider than this are + // translated into freeform gestures. + mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; + + // Abort current pointer usages because the state has changed. + abortPointerUsage(when, 0 /*policyFlags*/); + } + + // Inform the dispatcher about the changes. + *outResetNeeded = true; + bumpGeneration(); + } +} + +void TouchInputMapper::dumpSurface(std::string& dump) { + dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); + dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); + dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); + dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); + dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); + dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); + dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); +} + +void TouchInputMapper::configureVirtualKeys() { + std::vector virtualKeyDefinitions; + getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + int32_t touchScreenLeft = mRawPointerAxes.x.minValue; + int32_t touchScreenTop = mRawPointerAxes.y.minValue; + int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); + int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); + + for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { + VirtualKey virtualKey; + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + int32_t dummyKeyMetaState; + uint32_t flags; + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, + &dummyKeyMetaState, &flags)) { + ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + continue; // drop the key + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitRight = + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitTop = + (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + virtualKey.hitBottom = + (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + mVirtualKeys.push_back(virtualKey); + } +} + +void TouchInputMapper::dumpVirtualKeys(std::string& dump) { + if (!mVirtualKeys.empty()) { + dump += INDENT3 "Virtual Keys:\n"; + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " + "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", + i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, + virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } +} + +void TouchInputMapper::parseCalibration() { + const PropertyMap& in = getDevice()->getConfiguration(); + Calibration& out = mCalibration; + + // Size + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; + String8 sizeCalibrationString; + if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { + if (sizeCalibrationString == "none") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } else if (sizeCalibrationString == "geometric") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } else if (sizeCalibrationString == "diameter") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; + } else if (sizeCalibrationString == "box") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; + } else if (sizeCalibrationString == "area") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; + } else if (sizeCalibrationString != "default") { + ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); + } + } + + out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); + out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); + out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); + + // Pressure + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; + String8 pressureCalibrationString; + if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { + if (pressureCalibrationString == "none") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } else if (pressureCalibrationString == "physical") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } else if (pressureCalibrationString == "amplitude") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else if (pressureCalibrationString != "default") { + ALOGW("Invalid value for touch.pressure.calibration: '%s'", + pressureCalibrationString.string()); + } + } + + out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); + + // Orientation + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; + String8 orientationCalibrationString; + if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { + if (orientationCalibrationString == "none") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } else if (orientationCalibrationString == "interpolated") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else if (orientationCalibrationString == "vector") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; + } else if (orientationCalibrationString != "default") { + ALOGW("Invalid value for touch.orientation.calibration: '%s'", + orientationCalibrationString.string()); + } + } + + // Distance + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; + String8 distanceCalibrationString; + if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + if (distanceCalibrationString == "none") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } else if (distanceCalibrationString == "scaled") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else if (distanceCalibrationString != "default") { + ALOGW("Invalid value for touch.distance.calibration: '%s'", + distanceCalibrationString.string()); + } + } + + out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; + String8 coverageCalibrationString; + if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + if (coverageCalibrationString == "none") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } else if (coverageCalibrationString == "box") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; + } else if (coverageCalibrationString != "default") { + ALOGW("Invalid value for touch.coverage.calibration: '%s'", + coverageCalibrationString.string()); + } + } +} + +void TouchInputMapper::resolveCalibration() { + // Size + if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } + } else { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } + + // Pressure + if (mRawPointerAxes.pressure.valid) { + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } + } else { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } + + // Orientation + if (mRawPointerAxes.orientation.valid) { + if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } + } else { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } + + // Distance + if (mRawPointerAxes.distance.valid) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } + } else { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } + + // Coverage + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { + mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } +} + +void TouchInputMapper::dumpCalibration(std::string& dump) { + dump += INDENT3 "Calibration:\n"; + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NONE: + dump += INDENT4 "touch.size.calibration: none\n"; + break; + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + dump += INDENT4 "touch.size.calibration: geometric\n"; + break; + case Calibration::SIZE_CALIBRATION_DIAMETER: + dump += INDENT4 "touch.size.calibration: diameter\n"; + break; + case Calibration::SIZE_CALIBRATION_BOX: + dump += INDENT4 "touch.size.calibration: box\n"; + break; + case Calibration::SIZE_CALIBRATION_AREA: + dump += INDENT4 "touch.size.calibration: area\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveSizeScale) { + dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); + } + + if (mCalibration.haveSizeBias) { + dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); + } + + if (mCalibration.haveSizeIsSummed) { + dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", + toString(mCalibration.sizeIsSummed)); + } + + // Pressure + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_NONE: + dump += INDENT4 "touch.pressure.calibration: none\n"; + break; + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + dump += INDENT4 "touch.pressure.calibration: physical\n"; + break; + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + dump += INDENT4 "touch.pressure.calibration: amplitude\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.havePressureScale) { + dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_NONE: + dump += INDENT4 "touch.orientation.calibration: none\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + dump += INDENT4 "touch.orientation.calibration: interpolated\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: + dump += INDENT4 "touch.orientation.calibration: vector\n"; + break; + default: + ALOG_ASSERT(false); + } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_NONE: + dump += INDENT4 "touch.distance.calibration: none\n"; + break; + case Calibration::DISTANCE_CALIBRATION_SCALED: + dump += INDENT4 "touch.distance.calibration: scaled\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveDistanceScale) { + dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); + } + + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_NONE: + dump += INDENT4 "touch.coverage.calibration: none\n"; + break; + case Calibration::COVERAGE_CALIBRATION_BOX: + dump += INDENT4 "touch.coverage.calibration: box\n"; + break; + default: + ALOG_ASSERT(false); + } +} + +void TouchInputMapper::dumpAffineTransformation(std::string& dump) { + dump += INDENT3 "Affine Transformation:\n"; + + dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); + dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); + dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); + dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); + dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); + dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); +} + +void TouchInputMapper::updateAffineTransformation() { + mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), + mSurfaceOrientation); +} + +void TouchInputMapper::reset(nsecs_t when) { + mCursorButtonAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + mTouchButtonAccumulator.reset(getDevice()); + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mRawStatesPending.clear(); + mCurrentRawState.clear(); + mCurrentCookedState.clear(); + mLastRawState.clear(); + mLastCookedState.clear(); + mPointerUsage = POINTER_USAGE_NONE; + mSentHoverEnter = false; + mHavePointerIds = false; + mCurrentMotionAborted = false; + mDownTime = 0; + + mCurrentVirtualKey.down = false; + + mPointerGesture.reset(); + mPointerSimple.reset(); + resetExternalStylus(); + + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } + + InputMapper::reset(when); +} + +void TouchInputMapper::resetExternalStylus() { + mExternalStylusState.clear(); + mExternalStylusId = -1; + mExternalStylusFusionTimeout = LLONG_MAX; + mExternalStylusDataPending = false; +} + +void TouchInputMapper::clearStylusDataPendingFlags() { + mExternalStylusDataPending = false; + mExternalStylusFusionTimeout = LLONG_MAX; +} + +void TouchInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void TouchInputMapper::sync(nsecs_t when) { + const RawState* last = + mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); + + // Push a new state. + mRawStatesPending.emplace_back(); + + RawState* next = &mRawStatesPending.back(); + next->clear(); + next->when = when; + + // Sync button state. + next->buttonState = + mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); + + // Sync scroll + next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); + next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); + mCursorScrollAccumulator.finishSync(); + + // Sync touch + syncTouch(when, next); + + // Assign pointer ids. + if (!mHavePointerIds) { + assignPointerIds(last, next); + } + +#if DEBUG_RAW_EVENTS + ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " + "hovering ids 0x%08x -> 0x%08x", + last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, + last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, + last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); +#endif + + processRawTouches(false /*timeout*/); +} + +void TouchInputMapper::processRawTouches(bool timeout) { + if (mDeviceMode == DEVICE_MODE_DISABLED) { + // Drop all input if the device is disabled. + mCurrentRawState.clear(); + mRawStatesPending.clear(); + return; + } + + // Drain any pending touch states. The invariant here is that the mCurrentRawState is always + // valid and must go through the full cook and dispatch cycle. This ensures that anything + // touching the current state will only observe the events that have been dispatched to the + // rest of the pipeline. + const size_t N = mRawStatesPending.size(); + size_t count; + for (count = 0; count < N; count++) { + const RawState& next = mRawStatesPending[count]; + + // A failure to assign the stylus id means that we're waiting on stylus data + // and so should defer the rest of the pipeline. + if (assignExternalStylusId(next, timeout)) { + break; + } + + // All ready to go. + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(next); + if (mCurrentRawState.when < mLastRawState.when) { + mCurrentRawState.when = mLastRawState.when; + } + cookAndDispatch(mCurrentRawState.when); + } + if (count != 0) { + mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); + } + + if (mExternalStylusDataPending) { + if (timeout) { + nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(mLastRawState); +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, synthesizing event with new stylus data"); +#endif + cookAndDispatch(when); + } else if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::cookAndDispatch(nsecs_t when) { + // Always start with a clean state. + mCurrentCookedState.clear(); + + // Apply stylus buttons to current raw state. + applyExternalStylusButtonState(when); + + // Handle policy on initial down or hover events. + bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + mCurrentRawState.rawPointerData.pointerCount != 0; + + uint32_t policyFlags = 0; + bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; + if (initialDown || buttonsPressed) { + // If this is a touch screen, hide the pointer on an initial down. + if (mDeviceMode == DEVICE_MODE_DIRECT) { + getContext()->fadePointer(); + } + + if (mParameters.wake) { + policyFlags |= POLICY_FLAG_WAKE; + } + } + + // Consume raw off-screen touches before cooking pointer data. + // If touches are consumed, subsequent code will not receive any pointer data. + if (consumeRawTouches(when, policyFlags)) { + mCurrentRawState.rawPointerData.clear(); + } + + // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure + // with cooked pointer data that has the same ids and indices as the raw data. + // The following code can use either the raw or cooked data, as needed. + cookPointerData(); + + // Apply stylus pressure to current cooked state. + applyExternalStylusTouchState(when); + + // Synthesize key down from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Dispatch the touches either directly or by translation through a pointer on screen. + if (mDeviceMode == DEVICE_MODE_POINTER) { + for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mCurrentCookedState.fingerIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { + mCurrentCookedState.mouseIdBits.markBit(id); + } + } + for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } + } + + // Stylus takes precedence over all tools, then mouse, then finger. + PointerUsage pointerUsage = mPointerUsage; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + mCurrentCookedState.mouseIdBits.clear(); + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_STYLUS; + } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_MOUSE; + } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || + isPointerDown(mCurrentRawState.buttonState)) { + pointerUsage = POINTER_USAGE_GESTURES; + } + + dispatchPointerUsage(when, policyFlags, pointerUsage); + } else { + if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && + mPointerController != nullptr) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); + } + + if (!mCurrentMotionAborted) { + dispatchButtonRelease(when, policyFlags); + dispatchHoverExit(when, policyFlags); + dispatchTouches(when, policyFlags); + dispatchHoverEnterAndMove(when, policyFlags); + dispatchButtonPress(when, policyFlags); + } + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentMotionAborted = false; + } + } + + // Synthesize key up from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Clear some transient state. + mCurrentRawState.rawVScroll = 0; + mCurrentRawState.rawHScroll = 0; + + // Copy current touch to last touch in preparation for the next cycle. + mLastRawState.copyFrom(mCurrentRawState); + mLastCookedState.copyFrom(mCurrentCookedState); +} + +void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { + mCurrentRawState.buttonState |= mExternalStylusState.buttons; + } +} + +void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { + CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; + const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + + if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { + float pressure = mExternalStylusState.pressure; + if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { + const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); + pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + + PointerProperties& properties = + currentPointerData.editPointerPropertiesWithId(mExternalStylusId); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + properties.toolType = mExternalStylusState.toolType; + } + } +} + +bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { + if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { + return false; + } + + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + state.rawPointerData.pointerCount != 0; + if (initialDown) { + if (mExternalStylusState.pressure != 0.0f) { +#if DEBUG_STYLUS_FUSION + ALOGD("Have both stylus and touch data, beginning fusion"); +#endif + mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + } else if (timeout) { +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, assuming touch is not a stylus."); +#endif + resetExternalStylus(); + } else { + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } +#if DEBUG_STYLUS_FUSION + ALOGD("No stylus data but stylus is connected, requesting timeout " + "(%" PRId64 "ms)", + mExternalStylusFusionTimeout); +#endif + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; + } + } + + // Check if the stylus pointer has gone up. + if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { +#if DEBUG_STYLUS_FUSION + ALOGD("Stylus pointer is going up"); +#endif + mExternalStylusId = -1; + } + + return false; +} + +void TouchInputMapper::timeoutExpired(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + if (mPointerUsage == POINTER_USAGE_GESTURES) { + dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); + } + } else if (mDeviceMode == DEVICE_MODE_DIRECT) { + if (mExternalStylusFusionTimeout < when) { + processRawTouches(true /*timeout*/); + } else if (mExternalStylusFusionTimeout != LLONG_MAX) { + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::updateExternalStylusState(const StylusState& state) { + mExternalStylusState.copyFrom(state); + if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { + // We're either in the middle of a fused stream of data or we're waiting on data before + // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus + // data. + mExternalStylusDataPending = true; + processRawTouches(false /*timeout*/); + } +} + +bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { + // Check for release of a virtual key. + if (mCurrentVirtualKey.down) { + if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + return true; + } + + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return true; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation but do not consume the touch yet. + // This is useful when the user swipes through from the virtual key area + // into the main display surface. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, + mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | + AKEY_EVENT_FLAG_CANCELED); + } + } + + if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer just went down. Check for virtual key press or off-screen touches. + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); + if (!isPointInsideSurface(pointer.x, pointer.y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; + mCurrentVirtualKey.ignored = + mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, + virtualKey->scanCode); + + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + } + } + return true; + } + } + + // Disable all virtual key touches that happen within a short time interval of the + // most recent touch within the screen area. The idea is to filter out stray + // virtual key presses when interacting with the touch screen. + // + // Problems we're trying to solve: + // + // 1. While scrolling a list or dragging the window shade, the user swipes down into a + // virtual key area that is implemented by a separate touch panel and accidentally + // triggers a virtual key. + // + // 2. While typing in the on screen keyboard, the user taps slightly outside the screen + // area and accidentally triggers a virtual key. This often happens when virtual keys + // are layed out below the screen near to where the on screen keyboard's space bar + // is displayed. + if (mConfig.virtualKeyQuietTime > 0 && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); + } + return false; +} + +void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags) { + int32_t keyCode = mCurrentVirtualKey.keyCode; + int32_t scanCode = mCurrentVirtualKey.scanCode; + nsecs_t downTime = mCurrentVirtualKey.downTime; + int32_t metaState = mContext->getGlobalMetaState(); + policyFlags |= POLICY_FLAG_VIRTUAL; + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); + getListener()->notifyKey(&args); +} + +void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + if (!currentIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mCurrentMotionAborted = true; + } +} + +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + if (currentIdBits == lastIdBits) { + if (!currentIdBits.isEmpty()) { + // No pointer id changes so this is a move event. + // The listener takes care of batching moves so we don't have to deal with that here. + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } else { + // 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 moveIdBits(lastIdBits.value & currentIdBits.value); + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = + updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, moveIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + + // Dispatch pointer up events. + while (!upIdBits.isEmpty()) { + uint32_t upId = upIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); + } + + // 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 && !moveIdBits.isEmpty()) { + ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + + // Dispatch pointer down events using the new pointer locations. + while (!downIdBits.isEmpty()) { + uint32_t downId = downIdBits.clearFirstMarkedBit(); + dispatchedIdBits.markBit(downId); + + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. + mDownTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, + downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } +} + +void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { + if (mSentHoverEnter && + (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || + !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { + int32_t metaState = getContext()->getGlobalMetaState(); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastCookedState.buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, + mOrientedYPrecision, mDownTime); + mSentHoverEnter = false; + } +} + +void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { + if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && + !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + if (!mSentHoverEnter) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mSentHoverEnter = true; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { + BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!releasedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); + buttonState &= ~actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + actionButton, 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { + BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!pressedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); + buttonState |= actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, + 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { + if (!cookedPointerData.touchingIdBits.isEmpty()) { + return cookedPointerData.touchingIdBits; + } + return cookedPointerData.hoveringIdBits; +} + +void TouchInputMapper::cookPointerData() { + uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; + + mCurrentCookedState.cookedPointerData.clear(); + mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; + mCurrentCookedState.cookedPointerData.hoveringIdBits = + mCurrentRawState.rawPointerData.hoveringIdBits; + mCurrentCookedState.cookedPointerData.touchingIdBits = + mCurrentRawState.rawPointerData.touchingIdBits; + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentCookedState.buttonState = 0; + } else { + mCurrentCookedState.buttonState = mCurrentRawState.buttonState; + } + + // Walk through the the active pointers and map device coordinates onto + // surface coordinates and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; + + // Size + float touchMajor, touchMinor, toolMajor, toolMinor, size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + case Calibration::SIZE_CALIBRATION_DIAMETER: + case Calibration::SIZE_CALIBRATION_BOX: + case Calibration::SIZE_CALIBRATION_AREA: + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { + touchMajor = in.touchMajor; + touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + toolMajor = in.toolMajor; + toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.touchMajor.valid) { + toolMajor = touchMajor = in.touchMajor; + toolMinor = touchMinor = + mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.toolMajor.valid) { + touchMajor = toolMajor = in.toolMajor; + touchMinor = toolMinor = + mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + } else { + ALOG_ASSERT(false, + "No touch or tool axes. " + "Size calibration should have been resolved to NONE."); + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + } + + if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { + uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); + if (touchingCount > 1) { + touchMajor /= touchingCount; + touchMinor /= touchingCount; + toolMajor /= touchingCount; + toolMinor /= touchingCount; + size /= touchingCount; + } + } + + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { + touchMajor *= mGeometricScale; + touchMinor *= mGeometricScale; + toolMajor *= mGeometricScale; + toolMinor *= mGeometricScale; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { + touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; + touchMinor = touchMajor; + toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; + toolMinor = toolMajor; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { + touchMinor = touchMajor; + toolMinor = toolMajor; + } + + mCalibration.applySizeScaleAndBias(&touchMajor); + mCalibration.applySizeScaleAndBias(&touchMinor); + mCalibration.applySizeScaleAndBias(&toolMajor); + mCalibration.applySizeScaleAndBias(&toolMinor); + size *= mSizeScale; + break; + default: + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + break; + } + + // Pressure + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = in.pressure * mPressureScale; + break; + default: + pressure = in.isHovering ? 0 : 1; + break; + } + + // Tilt and Orientation + float tilt; + float orientation; + if (mHaveTilt) { + float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; + float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; + orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); + tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); + } else { + tilt = 0; + + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mOrientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float confidence = hypotf(c1, c2); + float scale = 1.0f + confidence / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; + } + break; + } + default: + orientation = 0; + } + } + + // Distance + float distance; + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_SCALED: + distance = in.distance * mDistanceScale; + break; + default: + distance = 0; + } + + // Coverage + int32_t rawLeft, rawTop, rawRight, rawBottom; + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_BOX: + rawLeft = (in.toolMinor & 0xffff0000) >> 16; + rawRight = in.toolMinor & 0x0000ffff; + rawBottom = in.toolMajor & 0x0000ffff; + rawTop = (in.toolMajor & 0xffff0000) >> 16; + break; + default: + rawLeft = rawTop = rawRight = rawBottom = 0; + break; + } + + // Adjust X,Y coords for device calibration + // TODO: Adjust coverage coords? + float xTransformed = in.x, yTransformed = in.y; + mAffineTransform.applyTo(xTransformed, yTransformed); + + // Adjust X, Y, and coverage coords for surface orientation. + float x, y; + float left, top, right, bottom; + + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + orientation -= M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; + y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + orientation -= M_PI; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; + y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + orientation += M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation > mOrientedRanges.orientation.max) { + orientation -= + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + default: + x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + break; + } + + // Write output coords. + PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); + out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); + } else { + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + } + + // Write output properties. + PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; + uint32_t id = in.id; + properties.clear(); + properties.id = id; + properties.toolType = in.toolType; + + // Write id index. + mCurrentCookedState.cookedPointerData.idToIndex[id] = i; + } +} + +void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, + PointerUsage pointerUsage) { + if (pointerUsage != mPointerUsage) { + abortPointerUsage(when, policyFlags); + mPointerUsage = pointerUsage; + } + + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); + break; + case POINTER_USAGE_STYLUS: + dispatchPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + dispatchPointerMouse(when, policyFlags); + break; + default: + break; + } +} + +void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + abortPointerGestures(when, policyFlags); + break; + case POINTER_USAGE_STYLUS: + abortPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + abortPointerMouse(when, policyFlags); + break; + default: + break; + } + + mPointerUsage = POINTER_USAGE_NONE; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + bool sendEvents = + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); + if (!sendEvents) { + return; + } + if (finishPreviousGesture) { + cancelPreviousGesture = false; + } + + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + + if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); + } + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } + + // Show or hide the pointer if needed. + switch (mPointerGesture.currentGestureMode) { + case PointerGesture::NEUTRAL: + case PointerGesture::QUIET: + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { + // Remind the user of where the pointer is after finishing a gesture with spots. + mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + } + break; + case PointerGesture::TAP: + case PointerGesture::TAP_DRAG: + case PointerGesture::BUTTON_CLICK_OR_DRAG: + case PointerGesture::HOVER: + case PointerGesture::PRESS: + case PointerGesture::SWIPE: + // Unfade the pointer when the current gesture manipulates the + // area directly under the pointer. + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + break; + case PointerGesture::FREEFORM: + // Fade the pointer when the current gesture manipulates a different + // area and there are spots to guide the user experience. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } else { + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + break; + } + + // Send events! + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || + mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE || + mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture && + !mPointerGesture.lastGestureIdBits.isEmpty() && + !mPointerGesture.currentGestureIdBits.isEmpty()) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & + mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = + dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; + } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); + } + } + } + + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & + ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.clearFirstMarkedBit(); + dispatchedGestureIdBits.markBit(id); + + if (dispatchedGestureIdBits.count() == 1) { + mPointerGesture.downTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + } + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + const int32_t displayId = mPointerController->getDisplayId(); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGestureIdBits.clear(); + } else { + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureProperties[index].copyFrom( + mPointerGesture.currentGestureProperties[index]); + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Cancel previously dispatches pointers. + if (!mPointerGesture.lastGestureIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentRawState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Reset the current pointer gesture. + mPointerGesture.reset(); + mPointerVelocityControl.reset(); + + // Remove any current spots. + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } +} + +bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + // Handle TAP timeout. + if (isTimeout) { +#if DEBUG_GESTURES + ALOGD("Gestures: Processing timeout"); +#endif + + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + // The tap/drag timeout has not yet expired. + getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + + mConfig.pointerGestureTapDragInterval); + } else { + // The tap is finished. +#if DEBUG_GESTURES + ALOGD("Gestures: TAP finished"); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + return true; + } + } + + // We did not handle this timeout. + return false; + } + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + positions[count].x = pointer.x * mPointerXMovementScale; + positions[count].y = pointer.y * mPointerYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, + positions); + } + + // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning + // to NEUTRAL, then we should not generate tap event. + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && + mPointerGesture.lastGestureMode != PointerGesture::TAP && + mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { + mPointerGesture.resetTap(); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We keep the same active touch id for as long as possible. + int32_t lastActiveTouchId = mPointerGesture.activeTouchId; + int32_t activeTouchId = lastActiveTouchId; + if (activeTouchId < 0) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + mPointerGesture.firstTouchTime = when; + } + } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; + } + } + + // Determine whether we are in quiet time. + bool isQuietTime = false; + if (activeTouchId < 0) { + mPointerGesture.resetQuietTime(); + } else { + isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || + mPointerGesture.lastGestureMode == PointerGesture::SWIPE || + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && + currentFingerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && + currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + ALOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + } else if (isPointerDown(mCurrentRawState.buttonState)) { + // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " + "currentFingerCount=%d", + activeTouchId, currentFingerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0 && currentFingerCount > 1) { + int32_t bestId = -1; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = hypotf(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", + bestId, bestSpeed); +#endif + } + } + + float deltaX = 0, deltaY = 0; + if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the click will occur at the position of the anchor + // spot and all other spots will move there. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (currentFingerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } + + // Watch for taps coming out of HOVER or TAP_DRAG mode. + // Checking for taps after TAP_DRAG allows us to detect double-taps. + bool tapped = false; + if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || + mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && + lastFingerCount == 1) { + if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP"); +#endif + + mPointerGesture.tapUpTime = when; + getContext()->requestTimeoutAtTime(when + + mConfig.pointerGestureTapDragInterval); + + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = + mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.tapX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.tapY); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + tapped = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, + y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + if (mPointerGesture.tapDownTime != LLONG_MIN) { + ALOGD("Gestures: Not a TAP, %0.3fms since down", + (when - mPointerGesture.tapDownTime) * 0.000001f); + } else { + ALOGD("Gestures: Not a TAP, incompatible mode transitions"); + } +#endif + } + } + + mPointerVelocityControl.reset(); + + if (!tapped) { +#if DEBUG_GESTURES + ALOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (currentFingerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) + // The pointer follows the active touch point. + // When in HOVER, emit HOVER_MOVE events at the pointer location. + // When in TAP_DRAG, emit MOVE events at the pointer location. + ALOG_ASSERT(activeTouchId >= 0); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", + (when - mPointerGesture.tapUpTime) * 0.000001f); +#endif + } + } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } + + float deltaX = 0, deltaY = 0; + if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the hover or drag will occur at the position of the anchor spot. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + bool down; + if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP_DRAG"); +#endif + down = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: HOVER"); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } + mPointerGesture.activeGestureId = 0; + down = false; + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + down ? 1.0f : 0.0f); + + if (lastFingerCount == 0 && currentFingerCount != 0) { + mPointerGesture.resetTap(); + mPointerGesture.tapDownTime = when; + mPointerGesture.tapX = x; + mPointerGesture.tapY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) + // We need to provide feedback for each finger that goes down so we cannot wait + // for the fingers to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they + // have not moved enough to determine whether they are part of a drag or part of a + // freeform gesture, or just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a + // down at the pointer location. + // + // When the two fingers move enough or when additional fingers are added, we make + // a decision to transition into SWIPE or FREEFORM mode accordingly. + ALOG_ASSERT(activeTouchId >= 0); + + bool settled = when >= + mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; + if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && + mPointerGesture.lastGestureMode != PointerGesture::SWIPE && + mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + *outFinishPreviousGesture = true; + } else if (!settled && currentFingerCount > lastFingerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + *outCancelPreviousGesture = true; + } else { + // Continue previous gesture. + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (*outFinishPreviousGesture || *outCancelPreviousGesture) { + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; + mPointerGesture.referenceIdBits.clear(); + mPointerVelocityControl.reset(); + + // Use the centroid and pointer location as the reference points for the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + mCurrentRawState.rawPointerData + .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & + ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); + const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; + } else { + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); + } + } + + // Consider transitions from PRESS to SWIPE or MULTITOUCH. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + if (currentFingerCount > 2) { + // There are more than two pointers, switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are exactly two pointers. + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + uint32_t id1 = idBits.clearFirstMarkedBit(); + uint32_t id2 = idBits.firstMarkedBit(); + const RawPointerData::Pointer& p1 = + mCurrentRawState.rawPointerData.pointerForId(id1); + const RawPointerData::Pointer& p2 = + mCurrentRawState.rawPointerData.pointerForId(id2); + float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); + if (mutualDistance > mPointerGestureMaxSwipeWidth) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + mutualDistance, mPointerGestureMaxSwipeWidth); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && + dist2 >= mConfig.pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * + // mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dx1 = delta1.dx * mPointerXZoomScale; + float dy1 = delta1.dy * mPointerYZoomScale; + float dx2 = delta2.dx * mPointerXZoomScale; + float dy2 = delta2.dy * mPointerYZoomScale; + float dot = dx1 * dx2 + dy1 * dy2; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + } + } + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (currentFingerCount > 2) { +#if DEBUG_GESTURES + ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && + (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx = 0; + delta.dy = 0; + } + + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; + + commonDeltaX *= mPointerXMovementScale; + commonDeltaY *= mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); + mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; + } + + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // PRESS or SWIPE mode. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & + ~mCurrentCookedState.fingerIdBits.value); + !upTouchIdBits.isEmpty();) { + uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } + +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif + + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + for (uint32_t i = 0; i < currentFingerCount; i++) { + uint32_t touchId = idBits.clearFirstMarkedBit(); + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.markFirstUnmarkedBit(); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(touchId); + float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; + float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + + mPointerGesture.currentGestureProperties[i].clear(); + mPointerGesture.currentGestureProperties[i].id = gestureId; + mPointerGesture.currentGestureProperties[i].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX + deltaX); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY + deltaY); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + 1.0f); + } + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM new " + "activeGestureId=%d", + mPointerGesture.activeGestureId); +#endif + } + } + } + + mPointerController->setButtonState(mCurrentRawState.buttonState); + +#if DEBUG_GESTURES + ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif + return true; +} + +void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); + uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; + float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); + float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); + mPointerController->setPosition(x, y); + + hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); + down = !hovering; + + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[index]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; + } else { + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); +} + +void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); + uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + float deltaX = 0, deltaY = 0; + if (mLastCookedState.mouseIdBits.hasBit(id)) { + uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - + mLastRawState.rawPointerData.pointers[lastIndex].x) * + mPointerXMovementScale; + deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - + mLastRawState.rawPointerData.pointers[lastIndex].y) * + mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + down = isPointerDown(mCurrentRawState.buttonState); + hovering = !down; + + float x, y; + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + hovering ? 0.0f : 1.0f); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; + } else { + mPointerVelocityControl.reset(); + + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); + + mPointerVelocityControl.reset(); +} + +void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, + bool hovering) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t displayId = mViewport.displayId; + + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + if (mPointerSimple.down && !down) { + mPointerSimple.down = false; + + // Send up. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mPointerSimple.hovering && !hovering) { + mPointerSimple.hovering = false; + + // Send hover exit. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (down) { + if (!mPointerSimple.down) { + mPointerSimple.down = true; + mPointerSimple.downTime = when; + + // Send down. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (hovering) { + if (!mPointerSimple.hovering) { + mPointerSimple.hovering = true; + + // Send hover enter. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send hover move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { + float vscroll = mCurrentRawState.rawVScroll; + float hscroll = mCurrentRawState.rawHScroll; + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + // Send scroll. + PointerCoords pointerCoords; + pointerCoords.copyFrom(mPointerSimple.currentCoords); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Save state. + if (down || hovering) { + mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); + mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); + } else { + mPointerSimple.reset(); + } +} + +void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + dispatchPointerSimple(when, policyFlags, false, false); +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, + const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, + BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + PointerProperties pointerProperties[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = idToIndex[id]; + pointerProperties[pointerCount].copyFrom(properties[index]); + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + ALOG_ASSERT(pointerCount != 0); + + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + ALOG_ASSERT(false); + } + } + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } + const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); + const int32_t deviceId = getDeviceId(); + std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::for_each(frames.begin(), frames.end(), + [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); + getListener()->notifyMotion(&args); +} + +bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, + const PointerCoords* inCoords, + const uint32_t* inIdToIndex, + PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +void TouchInputMapper::cancelTouch(nsecs_t when) { + abortPointerUsage(when, 0 /*policyFlags*/); + abortTouches(when, 0 /* policyFlags*/); +} + +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + const float scaledX = x * mXScale; + const float scaledY = y * mYScale; + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && + scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && + scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; +} + +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { + for (const VirtualKey& virtualKey : mVirtualKeys) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return &virtualKey; + } + } + + return nullptr; +} + +void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { + uint32_t currentPointerCount = current->rawPointerData.pointerCount; + uint32_t lastPointerCount = last->rawPointerData.pointerCount; + + current->rawPointerData.clearIdBits(); + + if (currentPointerCount == 0) { + // No pointers to assign. + return; + } + + if (lastPointerCount == 0) { + // All pointers are new. + for (uint32_t i = 0; i < currentPointerCount; i++) { + uint32_t id = i; + current->rawPointerData.pointers[i].id = id; + current->rawPointerData.idToIndex[id] = i; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); + } + return; + } + + if (currentPointerCount == 1 && lastPointerCount == 1 && + current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = last->rawPointerData.pointers[0].id; + current->rawPointerData.pointers[0].id = id; + current->rawPointerData.idToIndex[id] = 0; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); + return; + } + + // General case. + // 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. + // The pointers must have the same tool type but it is possible for them to + // transition from hovering to touching or vice-versa while retaining the same id. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + const RawPointerData::Pointer& currentPointer = + current->rawPointerData.pointers[currentPointerIndex]; + const RawPointerData::Pointer& lastPointer = + last->rawPointerData.pointers[lastPointerIndex]; + if (currentPointer.toolType == lastPointer.toolType) { + int64_t deltaX = currentPointer.x - lastPointer.x; + int64_t deltaY = currentPointer.y - lastPointer.y; + + 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; + } + } + } + + // 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; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, 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. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { + while (heapSize > 0) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0;;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize && + heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, + heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + } +#endif + } + + heapSize -= 1; + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering( + currentPointerIndex)); + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 + ", distance=%" PRIu64, + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to pointers that were not matched in the process. + for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { + uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); + uint32_t id = usedIdBits.markFirstUnmarkedBit(); + + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering(currentPointerIndex)); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); +#endif + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + for (const VirtualKey& virtualKey : mVirtualKeys) { + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + + return true; +} + +std::optional TouchInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + return std::make_optional(mViewport.displayId); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/VibratorInputMapper.cpp b/services/inputflinger/reader/VibratorInputMapper.cpp new file mode 100644 index 0000000000..a27fab4581 --- /dev/null +++ b/services/inputflinger/reader/VibratorInputMapper.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "VibratorInputMapper.h" + +namespace android { + +VibratorInputMapper::VibratorInputMapper(InputDevice* device) + : InputMapper(device), mVibrating(false) {} + +VibratorInputMapper::~VibratorInputMapper() {} + +uint32_t VibratorInputMapper::getSources() { + return 0; +} + +void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setVibrator(true); +} + +void VibratorInputMapper::process(const RawEvent* rawEvent) { + // TODO: Handle FF_STATUS, although it does not seem to be widely supported. +} + +void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +#if DEBUG_VIBRATOR + std::string patternStr; + for (size_t i = 0; i < patternSize; i++) { + if (i != 0) { + patternStr += ", "; + } + patternStr += StringPrintf("%" PRId64, pattern[i]); + } + ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), + patternStr.c_str(), repeat, token); +#endif + + mVibrating = true; + memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); + mPatternSize = patternSize; + mRepeat = repeat; + mToken = token; + mIndex = -1; + + nextStep(); +} + +void VibratorInputMapper::cancelVibrate(int32_t token) { +#if DEBUG_VIBRATOR + ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); +#endif + + if (mVibrating && mToken == token) { + stopVibrating(); + } +} + +void VibratorInputMapper::timeoutExpired(nsecs_t when) { + if (mVibrating) { + if (when >= mNextStepTime) { + nextStep(); + } else { + getContext()->requestTimeoutAtTime(mNextStepTime); + } + } +} + +void VibratorInputMapper::nextStep() { + mIndex += 1; + if (size_t(mIndex) >= mPatternSize) { + if (mRepeat < 0) { + // We are done. + stopVibrating(); + return; + } + mIndex = mRepeat; + } + + bool vibratorOn = mIndex & 1; + nsecs_t duration = mPattern[mIndex]; + if (vibratorOn) { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); +#endif + getEventHub()->vibrate(getDeviceId(), duration); + } else { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); + } + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mNextStepTime = now + duration; + getContext()->requestTimeoutAtTime(mNextStepTime); +#if DEBUG_VIBRATOR + ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); +#endif +} + +void VibratorInputMapper::stopVibrating() { + mVibrating = false; +#if DEBUG_VIBRATOR + ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); +} + +void VibratorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Vibrator Input Mapper:\n"; + dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); +} + +} // namespace android diff --git a/services/inputflinger/reader/VibratorInputMapper.h b/services/inputflinger/reader/VibratorInputMapper.h new file mode 100644 index 0000000000..6b33f4811e --- /dev/null +++ b/services/inputflinger/reader/VibratorInputMapper.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class VibratorInputMapper : public InputMapper { +public: + explicit VibratorInputMapper(InputDevice* device); + virtual ~VibratorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void process(const RawEvent* rawEvent); + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void timeoutExpired(nsecs_t when); + virtual void dump(std::string& dump); + +private: + bool mVibrating; + nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; + size_t mPatternSize; + ssize_t mRepeat; + int32_t mToken; + ssize_t mIndex; + nsecs_t mNextStepTime; + + void nextStep(); + void stopVibrating(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorButtonAccumulator.h b/services/inputflinger/reader/include/CursorButtonAccumulator.h new file mode 100644 index 0000000000..d9123109a3 --- /dev/null +++ b/services/inputflinger/reader/include/CursorButtonAccumulator.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of mouse or touch pad buttons. */ +class CursorButtonAccumulator { +public: + CursorButtonAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + +private: + bool mBtnLeft; + bool mBtnRight; + bool mBtnMiddle; + bool mBtnBack; + bool mBtnSide; + bool mBtnForward; + bool mBtnExtra; + bool mBtnTask; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorInputMapper.h b/services/inputflinger/reader/include/CursorInputMapper.h new file mode 100644 index 0000000000..52e42a7991 --- /dev/null +++ b/services/inputflinger/reader/include/CursorInputMapper.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +#include +#include + +namespace android { + +class VelocityControl; +class PointerControllerInterface; + +class CursorButtonAccumulator; +class CursorScrollAccumulator; + +/* Keeps track of cursor movements. */ +class CursorMotionAccumulator { +public: + CursorMotionAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + +private: + int32_t mRelX; + int32_t mRelY; + + void clearRelativeAxes(); +}; + +class CursorInputMapper : public InputMapper { +public: + explicit CursorInputMapper(InputDevice* device); + virtual ~CursorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + + virtual void fadePointer(); + + virtual std::optional getAssociatedDisplayId(); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + // Immutable configuration parameters. + struct Parameters { + enum Mode { + MODE_POINTER, + MODE_POINTER_RELATIVE, + MODE_NAVIGATION, + }; + + Mode mode; + bool hasAssociatedDisplay; + bool orientationAware; + } mParameters; + + CursorButtonAccumulator mCursorButtonAccumulator; + CursorMotionAccumulator mCursorMotionAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + + int32_t mSource; + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + float mVWheelScale; + float mHWheelScale; + + // Velocity controls for mouse pointer and wheel movements. + // The controls for X and Y wheel movements are separate to keep them decoupled. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + int32_t mOrientation; + + sp mPointerController; + + int32_t mButtonState; + nsecs_t mDownTime; + + void configureParameters(); + void dumpParameters(std::string& dump); + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorScrollAccumulator.h b/services/inputflinger/reader/include/CursorScrollAccumulator.h new file mode 100644 index 0000000000..85f331fd8a --- /dev/null +++ b/services/inputflinger/reader/include/CursorScrollAccumulator.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of cursor scrolling motions. */ + +class CursorScrollAccumulator { +public: + CursorScrollAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline bool haveRelativeVWheel() const { return mHaveRelWheel; } + inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + inline int32_t getRelativeVWheel() const { return mRelWheel; } + inline int32_t getRelativeHWheel() const { return mRelHWheel; } + +private: + bool mHaveRelWheel; + bool mHaveRelHWheel; + + int32_t mRelX; + int32_t mRelY; + int32_t mRelWheel; + int32_t mRelHWheel; + + void clearRelativeAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index da0f497b90..af83d2da67 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -14,7 +14,6 @@ * limitations under the License. */ -// #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h new file mode 100644 index 0000000000..882407dd20 --- /dev/null +++ b/services/inputflinger/reader/include/InputDevice.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_DEVICE_H +#define _UI_INPUTREADER_INPUT_DEVICE_H + +#include "EventHub.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" + +#include +#include +#include + +#include +#include +#include + +namespace android { + +class InputMapper; + +/* Represents the state of a single input device. */ +class InputDevice { +public: + InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes); + ~InputDevice(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() const { return mId; } + inline int32_t getControllerNumber() const { return mControllerNumber; } + inline int32_t getGeneration() const { return mGeneration; } + inline const std::string getName() const { return mIdentifier.name; } + inline const std::string getDescriptor() { return mIdentifier.descriptor; } + inline uint32_t getClasses() const { return mClasses; } + inline uint32_t getSources() const { return mSources; } + + inline bool isExternal() { return mIsExternal; } + inline void setExternal(bool external) { mIsExternal = external; } + inline std::optional getAssociatedDisplayPort() const { + return mAssociatedDisplayPort; + } + inline std::optional getAssociatedViewport() const { + return mAssociatedViewport; + } + inline void setMic(bool hasMic) { mHasMic = hasMic; } + inline bool hasMic() const { return mHasMic; } + + inline bool isIgnored() { return mMappers.empty(); } + + bool isEnabled(); + void setEnabled(bool enabled, nsecs_t when); + + void dump(std::string& dump); + void addMapper(InputMapper* mapper); + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + void reset(nsecs_t when); + void process(const RawEvent* rawEvents, size_t count); + void timeoutExpired(nsecs_t when); + void updateExternalStylusState(const StylusState& state); + + void getDeviceInfo(InputDeviceInfo* outDeviceInfo); + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags); + void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + void cancelVibrate(int32_t token); + void cancelTouch(nsecs_t when); + + int32_t getMetaState(); + void updateMetaState(int32_t keyCode); + + void fadePointer(); + + void bumpGeneration(); + + void notifyReset(nsecs_t when); + + inline const PropertyMap& getConfiguration() { return mConfiguration; } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); } + + bool hasAbsoluteAxis(int32_t code) { + RawAbsoluteAxisInfo info; + getEventHub()->getAbsoluteAxisInfo(mId, code, &info); + return info.valid; + } + + bool isKeyPressed(int32_t code) { + return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + } + + int32_t getAbsoluteAxisValue(int32_t code) { + int32_t value; + getEventHub()->getAbsoluteAxisValue(mId, code, &value); + return value; + } + + std::optional getAssociatedDisplayId(); + +private: + InputReaderContext* mContext; + int32_t mId; + int32_t mGeneration; + int32_t mControllerNumber; + InputDeviceIdentifier mIdentifier; + std::string mAlias; + uint32_t mClasses; + + std::vector mMappers; + + uint32_t mSources; + bool mIsExternal; + std::optional mAssociatedDisplayPort; + std::optional mAssociatedViewport; + bool mHasMic; + bool mDropUntilNextSync; + + typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); + + PropertyMap mConfiguration; +}; + +} // namespace android + +#endif //_UI_INPUTREADER_INPUT_DEVICE_H diff --git a/services/inputflinger/reader/include/InputMapper.h b/services/inputflinger/reader/include/InputMapper.h new file mode 100644 index 0000000000..a559ef882f --- /dev/null +++ b/services/inputflinger/reader/include/InputMapper.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_MAPPER_H +#define _UI_INPUTREADER_INPUT_MAPPER_H + +#include "EventHub.h" +#include "InputDevice.h" +#include "InputListener.h" +#include "InputReaderContext.h" +#include "StylusState.h" + +namespace android { + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + * + * InputMapper lifecycle: + * - create + * - configure with 0 changes + * - reset + * - process, process, process (may occasionally reconfigure with non-zero changes or reset) + * - reset + * - destroy + */ +class InputMapper { +public: + explicit InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const std::string getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputListenerInterface* getListener() { return mContext->getListener(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent) = 0; + virtual void timeoutExpired(nsecs_t when); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void cancelTouch(nsecs_t when); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + + virtual void updateExternalStylusState(const StylusState& state); + + virtual void fadePointer(); + virtual std::optional getAssociatedDisplayId() { return std::nullopt; } + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); + + static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name); + static void dumpStylusState(std::string& dump, const StylusState& state); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 1b8177c618..4288069659 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -14,89 +14,25 @@ * limitations under the License. */ -#ifndef _UI_INPUT_READER_H -#define _UI_INPUT_READER_H +#ifndef _UI_INPUTREADER_INPUT_READER_H +#define _UI_INPUTREADER_INPUT_READER_H #include "EventHub.h" #include "InputListener.h" #include "InputReaderBase.h" -#include "PointerControllerInterface.h" +#include "InputReaderContext.h" -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include -#include #include namespace android { class InputDevice; class InputMapper; - -struct StylusState { - /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; - /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; - /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; - - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } -}; - -/* Internal interface used by individual input devices to access global input device state - * and parameters maintained by the input reader. - */ -class InputReaderContext { -public: - InputReaderContext() {} - virtual ~InputReaderContext() {} - - virtual void updateGlobalMetaState() = 0; - virtual int32_t getGlobalMetaState() = 0; - - virtual void disableVirtualKeysUntil(nsecs_t time) = 0; - virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode) = 0; - - virtual void fadePointer() = 0; - - virtual void requestTimeoutAtTime(nsecs_t when) = 0; - virtual int32_t bumpGeneration() = 0; - - virtual void getExternalStylusDevices(std::vector& outDevices) = 0; - virtual void dispatchExternalStylusState(const StylusState& outState) = 0; - - virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; - virtual EventHubInterface* getEventHub() = 0; - - virtual uint32_t getNextSequenceNum() = 0; -}; +struct StylusState; /* The input reader reads raw event data from the event hub and processes it into input events * that it sends to the input listener. Some functions of the input reader, such as early @@ -239,1468 +175,6 @@ private: const int32_t* keyCodes, uint8_t* outFlags); }; -/* Represents the state of a single input device. */ -class InputDevice { -public: - InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes); - ~InputDevice(); - - inline InputReaderContext* getContext() { return mContext; } - inline int32_t getId() const { return mId; } - inline int32_t getControllerNumber() const { return mControllerNumber; } - inline int32_t getGeneration() const { return mGeneration; } - inline const std::string getName() const { return mIdentifier.name; } - inline const std::string getDescriptor() { return mIdentifier.descriptor; } - inline uint32_t getClasses() const { return mClasses; } - inline uint32_t getSources() const { return mSources; } - - inline bool isExternal() { return mIsExternal; } - inline void setExternal(bool external) { mIsExternal = external; } - inline std::optional getAssociatedDisplayPort() const { - return mAssociatedDisplayPort; - } - inline std::optional getAssociatedViewport() const { - return mAssociatedViewport; - } - inline void setMic(bool hasMic) { mHasMic = hasMic; } - inline bool hasMic() const { return mHasMic; } - - inline bool isIgnored() { return mMappers.empty(); } - - bool isEnabled(); - void setEnabled(bool enabled, nsecs_t when); - - void dump(std::string& dump); - void addMapper(InputMapper* mapper); - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - void reset(nsecs_t when); - void process(const RawEvent* rawEvents, size_t count); - void timeoutExpired(nsecs_t when); - void updateExternalStylusState(const StylusState& state); - - void getDeviceInfo(InputDeviceInfo* outDeviceInfo); - int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags); - void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - void cancelVibrate(int32_t token); - void cancelTouch(nsecs_t when); - - int32_t getMetaState(); - void updateMetaState(int32_t keyCode); - - void fadePointer(); - - void bumpGeneration(); - - void notifyReset(nsecs_t when); - - inline const PropertyMap& getConfiguration() { return mConfiguration; } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); } - - bool hasAbsoluteAxis(int32_t code) { - RawAbsoluteAxisInfo info; - getEventHub()->getAbsoluteAxisInfo(mId, code, &info); - return info.valid; - } - - bool isKeyPressed(int32_t code) { - return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; - } - - int32_t getAbsoluteAxisValue(int32_t code) { - int32_t value; - getEventHub()->getAbsoluteAxisValue(mId, code, &value); - return value; - } - - std::optional getAssociatedDisplayId(); - -private: - InputReaderContext* mContext; - int32_t mId; - int32_t mGeneration; - int32_t mControllerNumber; - InputDeviceIdentifier mIdentifier; - std::string mAlias; - uint32_t mClasses; - - std::vector mMappers; - - uint32_t mSources; - bool mIsExternal; - std::optional mAssociatedDisplayPort; - std::optional mAssociatedViewport; - bool mHasMic; - bool mDropUntilNextSync; - - typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); - int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); - - PropertyMap mConfiguration; -}; - -/* Keeps track of the state of mouse or touch pad buttons. */ -class CursorButtonAccumulator { -public: - CursorButtonAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - -private: - bool mBtnLeft; - bool mBtnRight; - bool mBtnMiddle; - bool mBtnBack; - bool mBtnSide; - bool mBtnForward; - bool mBtnExtra; - bool mBtnTask; - - void clearButtons(); -}; - -/* Keeps track of cursor movements. */ - -class CursorMotionAccumulator { -public: - CursorMotionAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - -private: - int32_t mRelX; - int32_t mRelY; - - void clearRelativeAxes(); -}; - -/* Keeps track of cursor scrolling motions. */ - -class CursorScrollAccumulator { -public: - CursorScrollAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline bool haveRelativeVWheel() const { return mHaveRelWheel; } - inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - inline int32_t getRelativeVWheel() const { return mRelWheel; } - inline int32_t getRelativeHWheel() const { return mRelHWheel; } - -private: - bool mHaveRelWheel; - bool mHaveRelHWheel; - - int32_t mRelX; - int32_t mRelY; - int32_t mRelWheel; - int32_t mRelHWheel; - - void clearRelativeAxes(); -}; - -/* Keeps track of the state of touch, stylus and tool buttons. */ -class TouchButtonAccumulator { -public: - TouchButtonAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - int32_t getToolType() const; - bool isToolActive() const; - bool isHovering() const; - bool hasStylus() const; - -private: - bool mHaveBtnTouch; - bool mHaveStylus; - - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; - - void clearButtons(); -}; - -/* Raw axis information from the driver. */ -struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); - inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } - inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); -}; - -/* Raw data for a collection of pointers including a pointer id mapping table. */ -struct RawPointerData { - struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; - }; - - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); - void getCentroidOfTouchingPointers(float* outX, float* outY) const; - - inline void markIdBit(uint32_t id, bool isHovering) { - if (isHovering) { - hoveringIdBits.markBit(id); - } else { - touchingIdBits.markBit(id); - } - } - - inline void clearIdBits() { - hoveringIdBits.clear(); - touchingIdBits.clear(); - } - - inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } - - inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } -}; - -/* Cooked data for a collection of pointers including a pointer id mapping table. */ -struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); - - inline const PointerCoords& pointerCoordsForId(uint32_t id) const { - return pointerCoords[idToIndex[id]]; - } - - inline PointerCoords& editPointerCoordsWithId(uint32_t id) { - return pointerCoords[idToIndex[id]]; - } - - inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { - return pointerProperties[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) const { - return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); - } - - inline bool isTouching(uint32_t pointerIndex) const { - return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); - } -}; - -/* Keeps track of the state of single-touch protocol. */ -class SingleTouchMotionAccumulator { -public: - SingleTouchMotionAccumulator(); - - void process(const RawEvent* rawEvent); - void reset(InputDevice* device); - - inline int32_t getAbsoluteX() const { return mAbsX; } - inline int32_t getAbsoluteY() const { return mAbsY; } - inline int32_t getAbsolutePressure() const { return mAbsPressure; } - inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } - inline int32_t getAbsoluteDistance() const { return mAbsDistance; } - inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } - inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } - -private: - int32_t mAbsX; - int32_t mAbsY; - int32_t mAbsPressure; - int32_t mAbsToolWidth; - int32_t mAbsDistance; - int32_t mAbsTiltX; - int32_t mAbsTiltY; - - void clearAbsoluteAxes(); -}; - -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; - } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; - } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse; - bool mHaveAbsMTTouchMinor; - bool mHaveAbsMTWidthMinor; - bool mHaveAbsMTToolType; - - int32_t mAbsMTPositionX; - int32_t mAbsMTPositionY; - int32_t mAbsMTTouchMajor; - int32_t mAbsMTTouchMinor; - int32_t mAbsMTWidthMajor; - int32_t mAbsMTWidthMinor; - int32_t mAbsMTOrientation; - int32_t mAbsMTTrackingId; - int32_t mAbsMTPressure; - int32_t mAbsMTDistance; - int32_t mAbsMTToolType; - - Slot(); - void clear(); - }; - - MultiTouchMotionAccumulator(); - ~MultiTouchMotionAccumulator(); - - void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); - void reset(InputDevice* device); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlotCount; } - inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - -private: - int32_t mCurrentSlot; - Slot* mSlots; - size_t mSlotCount; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void clearSlots(int32_t initialSlot); -}; - -/* An input mapper transforms raw input events into cooked event data. - * A single input device can have multiple associated input mappers in order to interpret - * different classes of events. - * - * InputMapper lifecycle: - * - create - * - configure with 0 changes - * - reset - * - process, process, process (may occasionally reconfigure with non-zero changes or reset) - * - reset - * - destroy - */ -class InputMapper { -public: - explicit InputMapper(InputDevice* device); - virtual ~InputMapper(); - - inline InputDevice* getDevice() { return mDevice; } - inline int32_t getDeviceId() { return mDevice->getId(); } - inline const std::string getDeviceName() { return mDevice->getName(); } - inline InputReaderContext* getContext() { return mContext; } - inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline InputListenerInterface* getListener() { return mContext->getListener(); } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - virtual uint32_t getSources() = 0; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent) = 0; - virtual void timeoutExpired(nsecs_t when); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void cancelTouch(nsecs_t when); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - - virtual void updateExternalStylusState(const StylusState& state); - - virtual void fadePointer(); - virtual std::optional getAssociatedDisplayId() { return std::nullopt; } - -protected: - InputDevice* mDevice; - InputReaderContext* mContext; - - status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); - void bumpGeneration(); - - static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, - const char* name); - static void dumpStylusState(std::string& dump, const StylusState& state); -}; - -class SwitchInputMapper : public InputMapper { -public: - explicit SwitchInputMapper(InputDevice* device); - virtual ~SwitchInputMapper(); - - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); - -private: - uint32_t mSwitchValues; - uint32_t mUpdatedSwitchMask; - - void processSwitch(int32_t switchCode, int32_t switchValue); - void sync(nsecs_t when); -}; - -class VibratorInputMapper : public InputMapper { -public: - explicit VibratorInputMapper(InputDevice* device); - virtual ~VibratorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); - -private: - bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; - ssize_t mRepeat; - int32_t mToken; - ssize_t mIndex; - nsecs_t mNextStepTime; - - void nextStep(); - void stopVibrating(); -}; - -class KeyboardInputMapper : public InputMapper { -public: - KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - virtual std::optional getAssociatedDisplayId(); - -private: - // The current viewport. - std::optional mViewport; - - struct KeyDown { - int32_t keyCode; - int32_t scanCode; - }; - - uint32_t mSource; - int32_t mKeyboardType; - - std::vector mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down - - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none - - struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on - }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; - - // Immutable configuration parameters. - struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - } mParameters; - - void configureParameters(); - void dumpParameters(std::string& dump); - - int32_t getOrientation(); - int32_t getDisplayId(); - - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - - void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); - - bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - - ssize_t findKeyDown(int32_t scanCode); - - void resetLedState(); - void initializeLedState(LedState& ledState, int32_t led); - void updateLedState(bool reset); - void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); - std::optional findViewport(nsecs_t when, - const InputReaderConfiguration* config); -}; - -class CursorInputMapper : public InputMapper { -public: - explicit CursorInputMapper(InputDevice* device); - virtual ~CursorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - - virtual void fadePointer(); - - virtual std::optional getAssociatedDisplayId(); - -private: - // Amount that trackball needs to move in order to generate a key event. - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; - - // Immutable configuration parameters. - struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, - }; - - Mode mode; - bool hasAssociatedDisplay; - bool orientationAware; - } mParameters; - - CursorButtonAccumulator mCursorButtonAccumulator; - CursorMotionAccumulator mCursorMotionAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - - int32_t mSource; - float mXScale; - float mYScale; - float mXPrecision; - float mYPrecision; - - float mVWheelScale; - float mHWheelScale; - - // Velocity controls for mouse pointer and wheel movements. - // The controls for X and Y wheel movements are separate to keep them decoupled. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - int32_t mOrientation; - - sp mPointerController; - - int32_t mButtonState; - nsecs_t mDownTime; - - void configureParameters(); - void dumpParameters(std::string& dump); - - void sync(nsecs_t when); -}; - -class RotaryEncoderInputMapper : public InputMapper { -public: - explicit RotaryEncoderInputMapper(InputDevice* device); - virtual ~RotaryEncoderInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - CursorScrollAccumulator mRotaryEncoderScrollAccumulator; - - int32_t mSource; - float mScalingFactor; - int32_t mOrientation; - - void sync(nsecs_t when); -}; - -class TouchInputMapper : public InputMapper { -public: - explicit TouchInputMapper(InputDevice* device); - virtual ~TouchInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional getAssociatedDisplayId(); - -protected: - CursorButtonAccumulator mCursorButtonAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - TouchButtonAccumulator mTouchButtonAccumulator; - - struct VirtualKey { - int32_t keyCode; - int32_t scanCode; - uint32_t flags; - - // computed hit box, specified in touch screen coords based on known display size - int32_t hitLeft; - int32_t hitTop; - int32_t hitRight; - int32_t hitBottom; - - inline bool isHit(int32_t x, int32_t y) const { - return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; - } - }; - - // Input sources and device mode. - uint32_t mSource; - - enum DeviceMode { - DEVICE_MODE_DISABLED, // input is disabled - DEVICE_MODE_DIRECT, // direct mapping (touchscreen) - DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) - DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - DEVICE_MODE_POINTER, // pointer mapping (pointer) - }; - DeviceMode mDeviceMode; - - // The reader's configuration. - InputReaderConfiguration mConfig; - - // Immutable configuration parameters. - struct Parameters { - enum DeviceType { - DEVICE_TYPE_TOUCH_SCREEN, - DEVICE_TYPE_TOUCH_PAD, - DEVICE_TYPE_TOUCH_NAVIGATION, - DEVICE_TYPE_POINTER, - }; - - DeviceType deviceType; - bool hasAssociatedDisplay; - bool associatedDisplayIsExternal; - bool orientationAware; - bool hasButtonUnderPad; - std::string uniqueDisplayId; - - enum GestureMode { - GESTURE_MODE_SINGLE_TOUCH, - GESTURE_MODE_MULTI_TOUCH, - }; - GestureMode gestureMode; - - bool wake; - } mParameters; - - // Immutable calibration parameters in parsed form. - struct Calibration { - // Size - enum SizeCalibration { - SIZE_CALIBRATION_DEFAULT, - SIZE_CALIBRATION_NONE, - SIZE_CALIBRATION_GEOMETRIC, - SIZE_CALIBRATION_DIAMETER, - SIZE_CALIBRATION_BOX, - SIZE_CALIBRATION_AREA, - }; - - SizeCalibration sizeCalibration; - - bool haveSizeScale; - float sizeScale; - bool haveSizeBias; - float sizeBias; - bool haveSizeIsSummed; - bool sizeIsSummed; - - // Pressure - enum PressureCalibration { - PRESSURE_CALIBRATION_DEFAULT, - PRESSURE_CALIBRATION_NONE, - PRESSURE_CALIBRATION_PHYSICAL, - PRESSURE_CALIBRATION_AMPLITUDE, - }; - - PressureCalibration pressureCalibration; - bool havePressureScale; - float pressureScale; - - // Orientation - enum OrientationCalibration { - ORIENTATION_CALIBRATION_DEFAULT, - ORIENTATION_CALIBRATION_NONE, - ORIENTATION_CALIBRATION_INTERPOLATED, - ORIENTATION_CALIBRATION_VECTOR, - }; - - OrientationCalibration orientationCalibration; - - // Distance - enum DistanceCalibration { - DISTANCE_CALIBRATION_DEFAULT, - DISTANCE_CALIBRATION_NONE, - DISTANCE_CALIBRATION_SCALED, - }; - - DistanceCalibration distanceCalibration; - bool haveDistanceScale; - float distanceScale; - - enum CoverageCalibration { - COVERAGE_CALIBRATION_DEFAULT, - COVERAGE_CALIBRATION_NONE, - COVERAGE_CALIBRATION_BOX, - }; - - CoverageCalibration coverageCalibration; - - inline void applySizeScaleAndBias(float* outSize) const { - if (haveSizeScale) { - *outSize *= sizeScale; - } - if (haveSizeBias) { - *outSize += sizeBias; - } - if (*outSize < 0) { - *outSize = 0; - } - } - } mCalibration; - - // Affine location transformation/calibration - struct TouchAffineTransformation mAffineTransform; - - RawPointerAxes mRawPointerAxes; - - struct RawState { - nsecs_t when; - - // Raw pointer sample data. - RawPointerData rawPointerData; - - int32_t buttonState; - - // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - void copyFrom(const RawState& other) { - when = other.when; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } - - void clear() { - when = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } - }; - - struct CookedState { - // Cooked pointer sample data. - CookedPointerData cookedPointerData; - - // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } - - void clear() { - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } - }; - - std::vector mRawStatesPending; - RawState mCurrentRawState; - CookedState mCurrentCookedState; - RawState mLastRawState; - CookedState mLastCookedState; - - // State provided by an external stylus - StylusState mExternalStylusState; - int64_t mExternalStylusId; - nsecs_t mExternalStylusFusionTimeout; - bool mExternalStylusDataPending; - - // True if we sent a HOVER_ENTER event. - bool mSentHoverEnter; - - // Have we assigned pointer IDs for this stream - bool mHavePointerIds; - - // Is the current stream of direct touch events aborted - bool mCurrentMotionAborted; - - // The time the primary pointer last went down. - nsecs_t mDownTime; - - // The pointer controller, or null if the device is not a pointer. - sp mPointerController; - - std::vector mVirtualKeys; - - virtual void configureParameters(); - virtual void dumpParameters(std::string& dump); - virtual void configureRawPointerAxes(); - virtual void dumpRawPointerAxes(std::string& dump); - virtual void configureSurface(nsecs_t when, bool* outResetNeeded); - virtual void dumpSurface(std::string& dump); - virtual void configureVirtualKeys(); - virtual void dumpVirtualKeys(std::string& dump); - virtual void parseCalibration(); - virtual void resolveCalibration(); - virtual void dumpCalibration(std::string& dump); - virtual void updateAffineTransformation(); - virtual void dumpAffineTransformation(std::string& dump); - virtual void resolveExternalStylusPresence(); - virtual bool hasStylus() const = 0; - virtual bool hasExternalStylus() const; - - virtual void syncTouch(nsecs_t when, RawState* outState) = 0; - -private: - // The current viewport. - // The components of the viewport are specified in the display's rotated orientation. - DisplayViewport mViewport; - - // The surface orientation, width and height set by configureSurface(). - // The width and height are derived from the viewport but are specified - // in the natural orientation. - // The surface origin specifies how the surface coordinates should be translated - // to align with the logical display coordinate space. - int32_t mSurfaceWidth; - int32_t mSurfaceHeight; - int32_t mSurfaceLeft; - int32_t mSurfaceTop; - - // Similar to the surface coordinates, but in the raw display coordinate space rather than in - // the logical coordinate space. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; - - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. - int32_t mSurfaceOrientation; - - // Translation and scaling factors, orientation-independent. - float mXTranslate; - float mXScale; - float mXPrecision; - - float mYTranslate; - float mYScale; - float mYPrecision; - - float mGeometricScale; - - float mPressureScale; - - float mSizeScale; - - float mOrientationScale; - - float mDistanceScale; - - bool mHaveTilt; - float mTiltXCenter; - float mTiltXScale; - float mTiltYCenter; - float mTiltYScale; - - bool mExternalStylusConnected; - - // Oriented motion ranges for input device info. - struct OrientedRanges { - InputDeviceInfo::MotionRange x; - InputDeviceInfo::MotionRange y; - InputDeviceInfo::MotionRange pressure; - - bool haveSize; - InputDeviceInfo::MotionRange size; - - bool haveTouchSize; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; - - bool haveToolSize; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; - - bool haveOrientation; - InputDeviceInfo::MotionRange orientation; - - bool haveDistance; - InputDeviceInfo::MotionRange distance; - - bool haveTilt; - InputDeviceInfo::MotionRange tilt; - - OrientedRanges() { clear(); } - - void clear() { - haveSize = false; - haveTouchSize = false; - haveToolSize = false; - haveOrientation = false; - haveDistance = false; - haveTilt = false; - } - } mOrientedRanges; - - // Oriented dimensions and precision. - float mOrientedXPrecision; - float mOrientedYPrecision; - - struct CurrentVirtualKeyState { - bool down; - bool ignored; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } mCurrentVirtualKey; - - // Scale factor for gesture or mouse based pointer movements. - float mPointerXMovementScale; - float mPointerYMovementScale; - - // Scale factor for gesture based zooming and other freeform motions. - float mPointerXZoomScale; - float mPointerYZoomScale; - - // The maximum swipe width. - float mPointerGestureMaxSwipeWidth; - - struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance - }; - - enum PointerUsage { - POINTER_USAGE_NONE, - POINTER_USAGE_GESTURES, - POINTER_USAGE_STYLUS, - POINTER_USAGE_MOUSE, - }; - PointerUsage mPointerUsage; - - struct PointerGesture { - enum Mode { - // No fingers, button is not pressed. - // Nothing happening. - NEUTRAL, - - // No fingers, button is not pressed. - // Tap detected. - // Emits DOWN and UP events at the pointer location. - TAP, - - // Exactly one finger dragging following a tap. - // Pointer follows the active finger. - // Emits DOWN, MOVE and UP events at the pointer location. - // - // Detect double-taps when the finger goes up while in TAP_DRAG mode. - TAP_DRAG, - - // Button is pressed. - // Pointer follows the active finger if there is one. Other fingers are ignored. - // Emits DOWN, MOVE and UP events at the pointer location. - BUTTON_CLICK_OR_DRAG, - - // Exactly one finger, button is not pressed. - // Pointer follows the active finger. - // Emits HOVER_MOVE events at the pointer location. - // - // Detect taps when the finger goes up while in HOVER mode. - HOVER, - - // Exactly two fingers but neither have moved enough to clearly indicate - // whether a swipe or freeform gesture was intended. We consider the - // pointer to be pressed so this enables clicking or long-pressing on buttons. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. - PRESS, - - // Exactly two fingers moving in the same direction, button is not pressed. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single pointer coordinate that - // follows the midpoint between both fingers. - SWIPE, - - // Two or more fingers moving in arbitrary directions, button is not pressed. - // Pointer does not move. - // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow - // each finger individually relative to the initial centroid of the finger. - FREEFORM, - - // Waiting for quiet time to end before starting the next gesture. - QUIET, - }; - - // Time the first finger went down. - nsecs_t firstTouchTime; - - // The active pointer id from the raw touch data. - int32_t activeTouchId; // -1 if none - - // The active pointer id from the gesture last delivered to the application. - int32_t activeGestureId; // -1 if none - - // Pointer coords and ids for the current and previous pointer gesture. - Mode currentGestureMode; - BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; - - Mode lastGestureMode; - BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; - - // Time the pointer gesture last went down. - nsecs_t downTime; - - // Time when the pointer went down for a TAP. - nsecs_t tapDownTime; - - // Time when the pointer went up for a TAP. - nsecs_t tapUpTime; - - // Location of initial tap. - float tapX, tapY; - - // Time we started waiting for quiescence. - nsecs_t quietTime; - - // Reference points for multitouch gestures. - float referenceTouchX; // reference touch X/Y coordinates in surface units - float referenceTouchY; - float referenceGestureX; // reference gesture X/Y coordinates in pixels - float referenceGestureY; - - // Distance that each pointer has traveled which has not yet been - // subsumed into the reference gesture position. - BitSet32 referenceIdBits; - struct Delta { - float dx, dy; - }; - Delta referenceDeltas[MAX_POINTER_ID + 1]; - - // Describes how touch ids are mapped to gesture ids for freeform gestures. - uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; - - // A velocity tracker for determining whether to switch active pointers during drags. - VelocityTracker velocityTracker; - - void reset() { - firstTouchTime = LLONG_MIN; - activeTouchId = -1; - activeGestureId = -1; - currentGestureMode = NEUTRAL; - currentGestureIdBits.clear(); - lastGestureMode = NEUTRAL; - lastGestureIdBits.clear(); - downTime = 0; - velocityTracker.clear(); - resetTap(); - resetQuietTime(); - } - - void resetTap() { - tapDownTime = LLONG_MIN; - tapUpTime = LLONG_MIN; - } - - void resetQuietTime() { quietTime = LLONG_MIN; } - } mPointerGesture; - - struct PointerSimple { - PointerCoords currentCoords; - PointerProperties currentProperties; - PointerCoords lastCoords; - PointerProperties lastProperties; - - // True if the pointer is down. - bool down; - - // True if the pointer is hovering. - bool hovering; - - // Time the pointer last went down. - nsecs_t downTime; - - void reset() { - currentCoords.clear(); - currentProperties.clear(); - lastCoords.clear(); - lastProperties.clear(); - down = false; - hovering = false; - downTime = 0; - } - } mPointerSimple; - - // The pointer and scroll velocity controls. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - std::optional findViewport(); - - void resetExternalStylus(); - void clearStylusDataPendingFlags(); - - void sync(nsecs_t when); - - bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); - void processRawTouches(bool timeout); - void cookAndDispatch(nsecs_t when); - void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, - int32_t keyEventFlags); - - void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); - void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); - void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); - void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); - const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); - void cookPointerData(); - void abortTouches(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); - void abortPointerUsage(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); - void abortPointerGestures(nsecs_t when, uint32_t policyFlags); - bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, - bool* outFinishPreviousGesture, bool isTimeout); - - void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); - void abortPointerStylus(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); - void abortPointerMouse(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); - void abortPointerSimple(nsecs_t when, uint32_t policyFlags); - - bool assignExternalStylusId(const RawState& state, bool timeout); - void applyExternalStylusButtonState(nsecs_t when); - void applyExternalStylusTouchState(nsecs_t when); - - // Dispatches a motion event. - // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the - // method will take care of setting the index and transmuting the action to DOWN or UP - // it is the first / last pointer to go down / up. - void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, - int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, - int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); - - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, - const uint32_t* inIdToIndex, PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const; - - bool isPointInsideSurface(int32_t x, int32_t y); - const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); - - static void assignPointerIds(const RawState* last, RawState* current); - - const char* modeToString(DeviceMode deviceMode); -}; - -class SingleTouchInputMapper : public TouchInputMapper { -public: - explicit SingleTouchInputMapper(InputDevice* device); - virtual ~SingleTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; -}; - -class MultiTouchInputMapper : public TouchInputMapper { -public: - explicit MultiTouchInputMapper(InputDevice* device); - virtual ~MultiTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; - - // Specifies the pointer id bits that are in use, and their associated tracking id. - BitSet32 mPointerIdBits; - int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; -}; - -class ExternalStylusInputMapper : public InputMapper { -public: - explicit ExternalStylusInputMapper(InputDevice* device); - virtual ~ExternalStylusInputMapper() = default; - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; - RawAbsoluteAxisInfo mRawPressureAxis; - TouchButtonAccumulator mTouchButtonAccumulator; - - StylusState mStylusState; -}; - -class JoystickInputMapper : public InputMapper { -public: - explicit JoystickInputMapper(InputDevice* device); - virtual ~JoystickInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - struct Axis { - RawAbsoluteAxisInfo rawAxisInfo; - AxisInfo axisInfo; - - bool explicitlyMapped; // true if the axis was explicitly assigned an axis id - - float scale; // scale factor from raw to normalized values - float offset; // offset to add after scaling for normalization - float highScale; // scale factor from raw to normalized values of high split - float highOffset; // offset to add after scaling for normalization of high split - - float min; // normalized inclusive minimum - float max; // normalized inclusive maximum - float flat; // normalized flat region size - float fuzz; // normalized error tolerance - float resolution; // normalized resolution in units/mm - - float filter; // filter out small variations of this size - float currentValue; // current value - float newValue; // most recent value - float highCurrentValue; // current value of high split - float highNewValue; // most recent value of high split - - void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, - bool explicitlyMapped, float scale, float offset, float highScale, - float highOffset, float min, float max, float flat, float fuzz, - float resolution) { - this->rawAxisInfo = rawAxisInfo; - this->axisInfo = axisInfo; - this->explicitlyMapped = explicitlyMapped; - this->scale = scale; - this->offset = offset; - this->highScale = highScale; - this->highOffset = highOffset; - this->min = min; - this->max = max; - this->flat = flat; - this->fuzz = fuzz; - this->resolution = resolution; - this->filter = 0; - resetValue(); - } - - void resetValue() { - this->currentValue = 0; - this->newValue = 0; - this->highCurrentValue = 0; - this->highNewValue = 0; - } - }; - - // Axes indexed by raw ABS_* axis index. - KeyedVector mAxes; - - void sync(nsecs_t when, bool force); - - bool haveAxis(int32_t axisId); - void pruneAxes(bool ignoreExplicitlyMappedAxes); - bool filterAxes(bool force); - - static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, - float min, float max); - static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, - float currentValue, float thresholdValue); - - static bool isCenteredAxis(int32_t axis); - static int32_t getCompatAxis(int32_t axis); - - static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); - static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); -}; - } // namespace android -#endif // _UI_INPUT_READER_H +#endif // _UI_INPUTREADER_INPUT_READER_H diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h new file mode 100644 index 0000000000..3472346d44 --- /dev/null +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H +#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H + +#include + +#include + +namespace android { + +class EventHubInterface; +class InputDevice; +class InputListenerInterface; +class InputMapper; +class InputReaderPolicyInterface; +struct StylusState; + +/* Internal interface used by individual input devices to access global input device state + * and parameters maintained by the input reader. + */ +class InputReaderContext { +public: + InputReaderContext() {} + virtual ~InputReaderContext() {} + + virtual void updateGlobalMetaState() = 0; + virtual int32_t getGlobalMetaState() = 0; + + virtual void disableVirtualKeysUntil(nsecs_t time) = 0; + virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode) = 0; + + virtual void fadePointer() = 0; + + virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual int32_t bumpGeneration() = 0; + + virtual void getExternalStylusDevices(std::vector& outDevices) = 0; + virtual void dispatchExternalStylusState(const StylusState& outState) = 0; + + virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputListenerInterface* getListener() = 0; + virtual EventHubInterface* getEventHub() = 0; + + virtual uint32_t getNextSequenceNum() = 0; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H diff --git a/services/inputflinger/reader/include/KeyboardInputMapper.h b/services/inputflinger/reader/include/KeyboardInputMapper.h new file mode 100644 index 0000000000..2665c0258c --- /dev/null +++ b/services/inputflinger/reader/include/KeyboardInputMapper.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H +#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + virtual std::optional getAssociatedDisplayId(); + +private: + // The current viewport. + std::optional mViewport; + + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + uint32_t mSource; + int32_t mKeyboardType; + + std::vector mKeyDowns; // keys that are down + int32_t mMetaState; + nsecs_t mDownTime; // time of most recent key down + + int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + + struct LedState { + bool avail; // led is available + bool on; // we think the led is currently on + }; + LedState mCapsLockLedState; + LedState mNumLockLedState; + LedState mScrollLockLedState; + + // Immutable configuration parameters. + struct Parameters { + bool orientationAware; + bool handlesKeyRepeat; + } mParameters; + + void configureParameters(); + void dumpParameters(std::string& dump); + + int32_t getOrientation(); + int32_t getDisplayId(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + bool isMediaKey(int32_t keyCode); + + void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); + + bool updateMetaStateIfNeeded(int32_t keyCode, bool down); + + ssize_t findKeyDown(int32_t scanCode); + + void resetLedState(); + void initializeLedState(LedState& ledState, int32_t led); + void updateLedState(bool reset); + void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); + std::optional findViewport(nsecs_t when, + const InputReaderConfiguration* config); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/MultiTouchInputMapper.h b/services/inputflinger/reader/include/MultiTouchInputMapper.h new file mode 100644 index 0000000000..87841ebeec --- /dev/null +++ b/services/inputflinger/reader/include/MultiTouchInputMapper.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H + +#include "TouchInputMapper.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMTPositionX; } + inline int32_t getY() const { return mAbsMTPositionY; } + inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMTOrientation; } + inline int32_t getTrackingId() const { return mAbsMTTrackingId; } + inline int32_t getPressure() const { return mAbsMTPressure; } + inline int32_t getDistance() const { return mAbsMTDistance; } + inline int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse; + bool mHaveAbsMTTouchMinor; + bool mHaveAbsMTWidthMinor; + bool mHaveAbsMTToolType; + + int32_t mAbsMTPositionX; + int32_t mAbsMTPositionY; + int32_t mAbsMTTouchMajor; + int32_t mAbsMTTouchMinor; + int32_t mAbsMTWidthMajor; + int32_t mAbsMTWidthMinor; + int32_t mAbsMTOrientation; + int32_t mAbsMTTrackingId; + int32_t mAbsMTPressure; + int32_t mAbsMTDistance; + int32_t mAbsMTToolType; + + Slot(); + void clear(); + }; + + MultiTouchMotionAccumulator(); + ~MultiTouchMotionAccumulator(); + + void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); + void reset(InputDevice* device); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlotCount; } + inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } + +private: + int32_t mCurrentSlot; + Slot* mSlots; + size_t mSlotCount; + bool mUsingSlotsProtocol; + bool mHaveStylus; + + void clearSlots(int32_t initialSlot); +}; + +class MultiTouchInputMapper : public TouchInputMapper { +public: + explicit MultiTouchInputMapper(InputDevice* device); + virtual ~MultiTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; + + // Specifies the pointer id bits that are in use, and their associated tracking id. + BitSet32 mPointerIdBits; + int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/SingleTouchInputMapper.h b/services/inputflinger/reader/include/SingleTouchInputMapper.h new file mode 100644 index 0000000000..d6b1455b68 --- /dev/null +++ b/services/inputflinger/reader/include/SingleTouchInputMapper.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H + +#include "SingleTouchMotionAccumulator.h" +#include "TouchInputMapper.h" + +namespace android { + +class SingleTouchInputMapper : public TouchInputMapper { +public: + explicit SingleTouchInputMapper(InputDevice* device); + virtual ~SingleTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h new file mode 100644 index 0000000000..75f8a961b3 --- /dev/null +++ b/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H +#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of single-touch protocol. */ +class SingleTouchMotionAccumulator { +public: + SingleTouchMotionAccumulator(); + + void process(const RawEvent* rawEvent); + void reset(InputDevice* device); + + inline int32_t getAbsoluteX() const { return mAbsX; } + inline int32_t getAbsoluteY() const { return mAbsY; } + inline int32_t getAbsolutePressure() const { return mAbsPressure; } + inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } + inline int32_t getAbsoluteDistance() const { return mAbsDistance; } + inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } + inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } + +private: + int32_t mAbsX; + int32_t mAbsY; + int32_t mAbsPressure; + int32_t mAbsToolWidth; + int32_t mAbsDistance; + int32_t mAbsTiltX; + int32_t mAbsTiltY; + + void clearAbsoluteAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h new file mode 100644 index 0000000000..17f158c9e1 --- /dev/null +++ b/services/inputflinger/reader/include/StylusState.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_STYLUS_STATE_H +#define _UI_INPUTREADER_STYLUS_STATE_H + +#include + +#include + +namespace android { + +struct StylusState { + /* Time the stylus event was received. */ + nsecs_t when; + /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ + float pressure; + /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ + uint32_t buttons; + /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ + int32_t toolType; + + void copyFrom(const StylusState& other) { + when = other.when; + pressure = other.pressure; + buttons = other.buttons; + toolType = other.toolType; + } + + void clear() { + when = LLONG_MAX; + pressure = 0.f; + buttons = 0; + toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + } +}; + +} // namespace android + +#endif // _UI_INPUTREADER_STYLUS_STATE_H diff --git a/services/inputflinger/reader/include/SwitchInputMapper.h b/services/inputflinger/reader/include/SwitchInputMapper.h new file mode 100644 index 0000000000..dd4bb9ed65 --- /dev/null +++ b/services/inputflinger/reader/include/SwitchInputMapper.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class SwitchInputMapper : public InputMapper { +public: + explicit SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual void dump(std::string& dump); + +private: + uint32_t mSwitchValues; + uint32_t mUpdatedSwitchMask; + + void processSwitch(int32_t switchCode, int32_t switchValue); + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/TouchButtonAccumulator.h b/services/inputflinger/reader/include/TouchButtonAccumulator.h new file mode 100644 index 0000000000..65b6bdcc12 --- /dev/null +++ b/services/inputflinger/reader/include/TouchButtonAccumulator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of touch, stylus and tool buttons. */ +class TouchButtonAccumulator { +public: + TouchButtonAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + int32_t getToolType() const; + bool isToolActive() const; + bool isHovering() const; + bool hasStylus() const; + +private: + bool mHaveBtnTouch; + bool mHaveStylus; + + bool mBtnTouch; + bool mBtnStylus; + bool mBtnStylus2; + bool mBtnToolFinger; + bool mBtnToolPen; + bool mBtnToolRubber; + bool mBtnToolBrush; + bool mBtnToolPencil; + bool mBtnToolAirbrush; + bool mBtnToolMouse; + bool mBtnToolLens; + bool mBtnToolDoubleTap; + bool mBtnToolTripleTap; + bool mBtnToolQuadTap; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/TouchInputMapper.h b/services/inputflinger/reader/include/TouchInputMapper.h new file mode 100644 index 0000000000..cf6cd76b87 --- /dev/null +++ b/services/inputflinger/reader/include/TouchInputMapper.h @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "EventHub.h" +#include "InputMapper.h" +#include "InputReaderBase.h" +#include "TouchButtonAccumulator.h" + +#include + +namespace android { + +/* Raw axis information from the driver. */ +struct RawPointerAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + RawAbsoluteAxisInfo distance; + RawAbsoluteAxisInfo tiltX; + RawAbsoluteAxisInfo tiltY; + RawAbsoluteAxisInfo trackingId; + RawAbsoluteAxisInfo slot; + + RawPointerAxes(); + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } + inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } + void clear(); +}; + +/* Raw data for a collection of pointers including a pointer id mapping table. */ +struct RawPointerData { + struct Pointer { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + int32_t distance; + int32_t tiltX; + int32_t tiltY; + int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant + bool isHovering; + }; + + uint32_t pointerCount; + Pointer pointers[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + RawPointerData(); + void clear(); + void copyFrom(const RawPointerData& other); + void getCentroidOfTouchingPointers(float* outX, float* outY) const; + + inline void markIdBit(uint32_t id, bool isHovering) { + if (isHovering) { + hoveringIdBits.markBit(id); + } else { + touchingIdBits.markBit(id); + } + } + + inline void clearIdBits() { + hoveringIdBits.clear(); + touchingIdBits.clear(); + } + + inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } + + inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } +}; + +/* Cooked data for a collection of pointers including a pointer id mapping table. */ +struct CookedPointerData { + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + CookedPointerData(); + void clear(); + void copyFrom(const CookedPointerData& other); + + inline const PointerCoords& pointerCoordsForId(uint32_t id) const { + return pointerCoords[idToIndex[id]]; + } + + inline PointerCoords& editPointerCoordsWithId(uint32_t id) { + return pointerCoords[idToIndex[id]]; + } + + inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { + return pointerProperties[idToIndex[id]]; + } + + inline bool isHovering(uint32_t pointerIndex) const { + return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); + } + + inline bool isTouching(uint32_t pointerIndex) const { + return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); + } +}; + +class TouchInputMapper : public InputMapper { +public: + explicit TouchInputMapper(InputDevice* device); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void fadePointer(); + virtual void cancelTouch(nsecs_t when); + virtual void timeoutExpired(nsecs_t when); + virtual void updateExternalStylusState(const StylusState& state); + virtual std::optional getAssociatedDisplayId(); + +protected: + CursorButtonAccumulator mCursorButtonAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + // Input sources and device mode. + uint32_t mSource; + + enum DeviceMode { + DEVICE_MODE_DISABLED, // input is disabled + DEVICE_MODE_DIRECT, // direct mapping (touchscreen) + DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) + DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) + DEVICE_MODE_POINTER, // pointer mapping (pointer) + }; + DeviceMode mDeviceMode; + + // The reader's configuration. + InputReaderConfiguration mConfig; + + // Immutable configuration parameters. + struct Parameters { + enum DeviceType { + DEVICE_TYPE_TOUCH_SCREEN, + DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_TOUCH_NAVIGATION, + DEVICE_TYPE_POINTER, + }; + + DeviceType deviceType; + bool hasAssociatedDisplay; + bool associatedDisplayIsExternal; + bool orientationAware; + bool hasButtonUnderPad; + std::string uniqueDisplayId; + + enum GestureMode { + GESTURE_MODE_SINGLE_TOUCH, + GESTURE_MODE_MULTI_TOUCH, + }; + GestureMode gestureMode; + + bool wake; + } mParameters; + + // Immutable calibration parameters in parsed form. + struct Calibration { + // Size + enum SizeCalibration { + SIZE_CALIBRATION_DEFAULT, + SIZE_CALIBRATION_NONE, + SIZE_CALIBRATION_GEOMETRIC, + SIZE_CALIBRATION_DIAMETER, + SIZE_CALIBRATION_BOX, + SIZE_CALIBRATION_AREA, + }; + + SizeCalibration sizeCalibration; + + bool haveSizeScale; + float sizeScale; + bool haveSizeBias; + float sizeBias; + bool haveSizeIsSummed; + bool sizeIsSummed; + + // Pressure + enum PressureCalibration { + PRESSURE_CALIBRATION_DEFAULT, + PRESSURE_CALIBRATION_NONE, + PRESSURE_CALIBRATION_PHYSICAL, + PRESSURE_CALIBRATION_AMPLITUDE, + }; + + PressureCalibration pressureCalibration; + bool havePressureScale; + float pressureScale; + + // Orientation + enum OrientationCalibration { + ORIENTATION_CALIBRATION_DEFAULT, + ORIENTATION_CALIBRATION_NONE, + ORIENTATION_CALIBRATION_INTERPOLATED, + ORIENTATION_CALIBRATION_VECTOR, + }; + + OrientationCalibration orientationCalibration; + + // Distance + enum DistanceCalibration { + DISTANCE_CALIBRATION_DEFAULT, + DISTANCE_CALIBRATION_NONE, + DISTANCE_CALIBRATION_SCALED, + }; + + DistanceCalibration distanceCalibration; + bool haveDistanceScale; + float distanceScale; + + enum CoverageCalibration { + COVERAGE_CALIBRATION_DEFAULT, + COVERAGE_CALIBRATION_NONE, + COVERAGE_CALIBRATION_BOX, + }; + + CoverageCalibration coverageCalibration; + + inline void applySizeScaleAndBias(float* outSize) const { + if (haveSizeScale) { + *outSize *= sizeScale; + } + if (haveSizeBias) { + *outSize += sizeBias; + } + if (*outSize < 0) { + *outSize = 0; + } + } + } mCalibration; + + // Affine location transformation/calibration + struct TouchAffineTransformation mAffineTransform; + + RawPointerAxes mRawPointerAxes; + + struct RawState { + nsecs_t when; + + // Raw pointer sample data. + RawPointerData rawPointerData; + + int32_t buttonState; + + // Scroll state. + int32_t rawVScroll; + int32_t rawHScroll; + + void copyFrom(const RawState& other) { + when = other.when; + rawPointerData.copyFrom(other.rawPointerData); + buttonState = other.buttonState; + rawVScroll = other.rawVScroll; + rawHScroll = other.rawHScroll; + } + + void clear() { + when = 0; + rawPointerData.clear(); + buttonState = 0; + rawVScroll = 0; + rawHScroll = 0; + } + }; + + struct CookedState { + // Cooked pointer sample data. + CookedPointerData cookedPointerData; + + // Id bits used to differentiate fingers, stylus and mouse tools. + BitSet32 fingerIdBits; + BitSet32 stylusIdBits; + BitSet32 mouseIdBits; + + int32_t buttonState; + + void copyFrom(const CookedState& other) { + cookedPointerData.copyFrom(other.cookedPointerData); + fingerIdBits = other.fingerIdBits; + stylusIdBits = other.stylusIdBits; + mouseIdBits = other.mouseIdBits; + buttonState = other.buttonState; + } + + void clear() { + cookedPointerData.clear(); + fingerIdBits.clear(); + stylusIdBits.clear(); + mouseIdBits.clear(); + buttonState = 0; + } + }; + + std::vector mRawStatesPending; + RawState mCurrentRawState; + CookedState mCurrentCookedState; + RawState mLastRawState; + CookedState mLastCookedState; + + // State provided by an external stylus + StylusState mExternalStylusState; + int64_t mExternalStylusId; + nsecs_t mExternalStylusFusionTimeout; + bool mExternalStylusDataPending; + + // True if we sent a HOVER_ENTER event. + bool mSentHoverEnter; + + // Have we assigned pointer IDs for this stream + bool mHavePointerIds; + + // Is the current stream of direct touch events aborted + bool mCurrentMotionAborted; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + // The pointer controller, or null if the device is not a pointer. + sp mPointerController; + + std::vector mVirtualKeys; + + virtual void configureParameters(); + virtual void dumpParameters(std::string& dump); + virtual void configureRawPointerAxes(); + virtual void dumpRawPointerAxes(std::string& dump); + virtual void configureSurface(nsecs_t when, bool* outResetNeeded); + virtual void dumpSurface(std::string& dump); + virtual void configureVirtualKeys(); + virtual void dumpVirtualKeys(std::string& dump); + virtual void parseCalibration(); + virtual void resolveCalibration(); + virtual void dumpCalibration(std::string& dump); + virtual void updateAffineTransformation(); + virtual void dumpAffineTransformation(std::string& dump); + virtual void resolveExternalStylusPresence(); + virtual bool hasStylus() const = 0; + virtual bool hasExternalStylus() const; + + virtual void syncTouch(nsecs_t when, RawState* outState) = 0; + +private: + // The current viewport. + // The components of the viewport are specified in the display's rotated orientation. + DisplayViewport mViewport; + + // The surface orientation, width and height set by configureSurface(). + // The width and height are derived from the viewport but are specified + // in the natural orientation. + // The surface origin specifies how the surface coordinates should be translated + // to align with the logical display coordinate space. + int32_t mSurfaceWidth; + int32_t mSurfaceHeight; + int32_t mSurfaceLeft; + int32_t mSurfaceTop; + + // Similar to the surface coordinates, but in the raw display coordinate space rather than in + // the logical coordinate space. + int32_t mPhysicalWidth; + int32_t mPhysicalHeight; + int32_t mPhysicalLeft; + int32_t mPhysicalTop; + + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. + int32_t mSurfaceOrientation; + + // Translation and scaling factors, orientation-independent. + float mXTranslate; + float mXScale; + float mXPrecision; + + float mYTranslate; + float mYScale; + float mYPrecision; + + float mGeometricScale; + + float mPressureScale; + + float mSizeScale; + + float mOrientationScale; + + float mDistanceScale; + + bool mHaveTilt; + float mTiltXCenter; + float mTiltXScale; + float mTiltYCenter; + float mTiltYScale; + + bool mExternalStylusConnected; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + + bool haveSize; + InputDeviceInfo::MotionRange size; + + bool haveTouchSize; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + + bool haveToolSize; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + + bool haveOrientation; + InputDeviceInfo::MotionRange orientation; + + bool haveDistance; + InputDeviceInfo::MotionRange distance; + + bool haveTilt; + InputDeviceInfo::MotionRange tilt; + + OrientedRanges() { clear(); } + + void clear() { + haveSize = false; + haveTouchSize = false; + haveToolSize = false; + haveOrientation = false; + haveDistance = false; + haveTilt = false; + } + } mOrientedRanges; + + // Oriented dimensions and precision. + float mOrientedXPrecision; + float mOrientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + bool ignored; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } mCurrentVirtualKey; + + // Scale factor for gesture or mouse based pointer movements. + float mPointerXMovementScale; + float mPointerYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float mPointerXZoomScale; + float mPointerYZoomScale; + + // The maximum swipe width. + float mPointerGestureMaxSwipeWidth; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + enum PointerUsage { + POINTER_USAGE_NONE, + POINTER_USAGE_GESTURES, + POINTER_USAGE_STYLUS, + POINTER_USAGE_MOUSE, + }; + PointerUsage mPointerUsage; + + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Exactly one finger dragging following a tap. + // Pointer follows the active finger. + // Emits DOWN, MOVE and UP events at the pointer location. + // + // Detect double-taps when the finger goes up while in TAP_DRAG mode. + TAP_DRAG, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + BUTTON_CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + // + // Detect taps when the finger goes up while in HOVER mode. + HOVER, + + // Exactly two fingers but neither have moved enough to clearly indicate + // whether a swipe or freeform gesture was intended. We consider the + // pointer to be pressed so this enables clicking or long-pressing on buttons. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. + PRESS, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // Time the first finger went down. + nsecs_t firstTouchTime; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties currentGestureProperties[MAX_POINTERS]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties lastGestureProperties[MAX_POINTERS]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time when the pointer went down for a TAP. + nsecs_t tapDownTime; + + // Time when the pointer went up for a TAP. + nsecs_t tapUpTime; + + // Location of initial tap. + float tapX, tapY; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // Reference points for multitouch gestures. + float referenceTouchX; // reference touch X/Y coordinates in surface units + float referenceTouchY; + float referenceGestureX; // reference gesture X/Y coordinates in pixels + float referenceGestureY; + + // Distance that each pointer has traveled which has not yet been + // subsumed into the reference gesture position. + BitSet32 referenceIdBits; + struct Delta { + float dx, dy; + }; + Delta referenceDeltas[MAX_POINTER_ID + 1]; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + firstTouchTime = LLONG_MIN; + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGestureIdBits.clear(); + downTime = 0; + velocityTracker.clear(); + resetTap(); + resetQuietTime(); + } + + void resetTap() { + tapDownTime = LLONG_MIN; + tapUpTime = LLONG_MIN; + } + + void resetQuietTime() { quietTime = LLONG_MIN; } + } mPointerGesture; + + struct PointerSimple { + PointerCoords currentCoords; + PointerProperties currentProperties; + PointerCoords lastCoords; + PointerProperties lastProperties; + + // True if the pointer is down. + bool down; + + // True if the pointer is hovering. + bool hovering; + + // Time the pointer last went down. + nsecs_t downTime; + + void reset() { + currentCoords.clear(); + currentProperties.clear(); + lastCoords.clear(); + lastProperties.clear(); + down = false; + hovering = false; + downTime = 0; + } + } mPointerSimple; + + // The pointer and scroll velocity controls. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + std::optional findViewport(); + + void resetExternalStylus(); + void clearStylusDataPendingFlags(); + + void sync(nsecs_t when); + + bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); + void processRawTouches(bool timeout); + void cookAndDispatch(nsecs_t when); + void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags); + + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); + void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); + void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); + void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); + const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); + void cookPointerData(); + void abortTouches(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); + void abortPointerUsage(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); + void abortPointerGestures(nsecs_t when, uint32_t policyFlags); + bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout); + + void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); + void abortPointerStylus(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); + void abortPointerMouse(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); + void abortPointerSimple(nsecs_t when, uint32_t policyFlags); + + bool assignExternalStylusId(const RawState& state, bool timeout); + void applyExternalStylusButtonState(nsecs_t when); + void applyExternalStylusTouchState(nsecs_t when); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords and properties for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, + const uint32_t* inIdToIndex, PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const; + + bool isPointInsideSurface(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); + + static void assignPointerIds(const RawState* last, RawState* current); + + const char* modeToString(DeviceMode deviceMode); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h index 9dfa951ff1..5a32443f29 100644 --- a/services/inputflinger/reader/include/TouchVideoDevice.h +++ b/services/inputflinger/reader/include/TouchVideoDevice.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H -#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H #include #include @@ -121,5 +121,7 @@ private: */ std::optional readFrame(); }; + } // namespace android -#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H + +#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 55c0497c4d..a9088bcf28 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -15,7 +15,15 @@ */ #include "InputReader.h" +#include "CursorInputMapper.h" +#include "InputDevice.h" +#include "InputMapper.h" +#include "KeyboardInputMapper.h" +#include "MultiTouchInputMapper.h" +#include "SingleTouchInputMapper.h" +#include "SwitchInputMapper.h" #include "TestInputListener.h" +#include "TouchInputMapper.h" #include #include -- cgit v1.2.3-59-g8ed1b From 2770d2473085bc0878e56cad5f5a4c75591d2118 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Mon, 2 Sep 2019 18:07:11 -0700 Subject: Create inputmappers and accumulators directories Inside the 'inputreader' directory, we create an 'mapper' directory to hold the InputMappers and a 'mapper/accumulator' directory to hold the Accumulators. Bug: 140139676 Test: atest inputflinger_tests Test: Touch, keyboard, trackpad, and mouse works on crosshatch Change-Id: I4966919cd5b42353dd8c1e7cc7663f6c83765542 --- services/inputflinger/reader/Android.bp | 36 +- .../reader/CursorButtonAccumulator.cpp | 101 - services/inputflinger/reader/CursorInputMapper.cpp | 479 --- .../reader/CursorScrollAccumulator.cpp | 59 - .../reader/ExternalStylusInputMapper.cpp | 92 - .../reader/ExternalStylusInputMapper.h | 51 - services/inputflinger/reader/InputMapper.cpp | 101 - .../inputflinger/reader/JoystickInputMapper.cpp | 409 --- services/inputflinger/reader/JoystickInputMapper.h | 111 - .../inputflinger/reader/KeyboardInputMapper.cpp | 433 --- .../inputflinger/reader/MultiTouchInputMapper.cpp | 357 -- .../reader/RotaryEncoderInputMapper.cpp | 134 - .../inputflinger/reader/RotaryEncoderInputMapper.h | 49 - .../inputflinger/reader/SingleTouchInputMapper.cpp | 85 - .../reader/SingleTouchMotionAccumulator.cpp | 76 - services/inputflinger/reader/SwitchInputMapper.cpp | 76 - .../inputflinger/reader/TouchButtonAccumulator.cpp | 162 - .../reader/TouchCursorInputMapperCommon.h | 88 - services/inputflinger/reader/TouchInputMapper.cpp | 3883 -------------------- .../inputflinger/reader/VibratorInputMapper.cpp | 131 - services/inputflinger/reader/VibratorInputMapper.h | 53 - .../reader/include/CursorButtonAccumulator.h | 52 - .../reader/include/CursorInputMapper.h | 123 - .../reader/include/CursorScrollAccumulator.h | 60 - services/inputflinger/reader/include/InputMapper.h | 92 - .../reader/include/KeyboardInputMapper.h | 102 - .../reader/include/MultiTouchInputMapper.h | 115 - .../reader/include/SingleTouchInputMapper.h | 44 - .../reader/include/SingleTouchMotionAccumulator.h | 57 - .../reader/include/SwitchInputMapper.h | 45 - .../reader/include/TouchButtonAccumulator.h | 66 - .../inputflinger/reader/include/TouchInputMapper.h | 764 ---- .../reader/mapper/CursorInputMapper.cpp | 479 +++ .../inputflinger/reader/mapper/CursorInputMapper.h | 123 + .../reader/mapper/ExternalStylusInputMapper.cpp | 92 + .../reader/mapper/ExternalStylusInputMapper.h | 51 + .../inputflinger/reader/mapper/InputMapper.cpp | 101 + services/inputflinger/reader/mapper/InputMapper.h | 92 + .../reader/mapper/JoystickInputMapper.cpp | 409 +++ .../reader/mapper/JoystickInputMapper.h | 111 + .../reader/mapper/KeyboardInputMapper.cpp | 433 +++ .../reader/mapper/KeyboardInputMapper.h | 102 + .../reader/mapper/MultiTouchInputMapper.cpp | 357 ++ .../reader/mapper/MultiTouchInputMapper.h | 115 + .../reader/mapper/RotaryEncoderInputMapper.cpp | 134 + .../reader/mapper/RotaryEncoderInputMapper.h | 49 + .../reader/mapper/SingleTouchInputMapper.cpp | 85 + .../reader/mapper/SingleTouchInputMapper.h | 44 + .../reader/mapper/SwitchInputMapper.cpp | 76 + .../inputflinger/reader/mapper/SwitchInputMapper.h | 45 + .../reader/mapper/TouchCursorInputMapperCommon.h | 88 + .../reader/mapper/TouchInputMapper.cpp | 3883 ++++++++++++++++++++ .../inputflinger/reader/mapper/TouchInputMapper.h | 764 ++++ .../reader/mapper/VibratorInputMapper.cpp | 131 + .../reader/mapper/VibratorInputMapper.h | 53 + .../mapper/accumulator/CursorButtonAccumulator.cpp | 101 + .../mapper/accumulator/CursorButtonAccumulator.h | 52 + .../mapper/accumulator/CursorScrollAccumulator.cpp | 59 + .../mapper/accumulator/CursorScrollAccumulator.h | 60 + .../accumulator/SingleTouchMotionAccumulator.cpp | 76 + .../accumulator/SingleTouchMotionAccumulator.h | 57 + .../mapper/accumulator/TouchButtonAccumulator.cpp | 162 + .../mapper/accumulator/TouchButtonAccumulator.h | 66 + services/inputflinger/tests/InputReader_test.cpp | 20 +- 64 files changed, 8480 insertions(+), 8476 deletions(-) delete mode 100644 services/inputflinger/reader/CursorButtonAccumulator.cpp delete mode 100644 services/inputflinger/reader/CursorInputMapper.cpp delete mode 100644 services/inputflinger/reader/CursorScrollAccumulator.cpp delete mode 100644 services/inputflinger/reader/ExternalStylusInputMapper.cpp delete mode 100644 services/inputflinger/reader/ExternalStylusInputMapper.h delete mode 100644 services/inputflinger/reader/InputMapper.cpp delete mode 100644 services/inputflinger/reader/JoystickInputMapper.cpp delete mode 100644 services/inputflinger/reader/JoystickInputMapper.h delete mode 100644 services/inputflinger/reader/KeyboardInputMapper.cpp delete mode 100644 services/inputflinger/reader/MultiTouchInputMapper.cpp delete mode 100644 services/inputflinger/reader/RotaryEncoderInputMapper.cpp delete mode 100644 services/inputflinger/reader/RotaryEncoderInputMapper.h delete mode 100644 services/inputflinger/reader/SingleTouchInputMapper.cpp delete mode 100644 services/inputflinger/reader/SingleTouchMotionAccumulator.cpp delete mode 100644 services/inputflinger/reader/SwitchInputMapper.cpp delete mode 100644 services/inputflinger/reader/TouchButtonAccumulator.cpp delete mode 100644 services/inputflinger/reader/TouchCursorInputMapperCommon.h delete mode 100644 services/inputflinger/reader/TouchInputMapper.cpp delete mode 100644 services/inputflinger/reader/VibratorInputMapper.cpp delete mode 100644 services/inputflinger/reader/VibratorInputMapper.h delete mode 100644 services/inputflinger/reader/include/CursorButtonAccumulator.h delete mode 100644 services/inputflinger/reader/include/CursorInputMapper.h delete mode 100644 services/inputflinger/reader/include/CursorScrollAccumulator.h delete mode 100644 services/inputflinger/reader/include/InputMapper.h delete mode 100644 services/inputflinger/reader/include/KeyboardInputMapper.h delete mode 100644 services/inputflinger/reader/include/MultiTouchInputMapper.h delete mode 100644 services/inputflinger/reader/include/SingleTouchInputMapper.h delete mode 100644 services/inputflinger/reader/include/SingleTouchMotionAccumulator.h delete mode 100644 services/inputflinger/reader/include/SwitchInputMapper.h delete mode 100644 services/inputflinger/reader/include/TouchButtonAccumulator.h delete mode 100644 services/inputflinger/reader/include/TouchInputMapper.h create mode 100644 services/inputflinger/reader/mapper/CursorInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/CursorInputMapper.h create mode 100644 services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/ExternalStylusInputMapper.h create mode 100644 services/inputflinger/reader/mapper/InputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/InputMapper.h create mode 100644 services/inputflinger/reader/mapper/JoystickInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/JoystickInputMapper.h create mode 100644 services/inputflinger/reader/mapper/KeyboardInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/KeyboardInputMapper.h create mode 100644 services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/MultiTouchInputMapper.h create mode 100644 services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h create mode 100644 services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/SingleTouchInputMapper.h create mode 100644 services/inputflinger/reader/mapper/SwitchInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/SwitchInputMapper.h create mode 100644 services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h create mode 100644 services/inputflinger/reader/mapper/TouchInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/TouchInputMapper.h create mode 100644 services/inputflinger/reader/mapper/VibratorInputMapper.cpp create mode 100644 services/inputflinger/reader/mapper/VibratorInputMapper.h create mode 100644 services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp create mode 100644 services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h create mode 100644 services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp create mode 100644 services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h create mode 100644 services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp create mode 100644 services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h create mode 100644 services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp create mode 100644 services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 7b307651d5..a64f4ddc47 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -14,7 +14,11 @@ cc_library_headers { name: "libinputreader_headers", - export_include_dirs: ["include"], + export_include_dirs: [ + "include", + "mapper", + "mapper/accumulator", + ], } cc_library_shared { @@ -22,26 +26,26 @@ cc_library_shared { defaults: ["inputflinger_defaults"], srcs: [ - "CursorButtonAccumulator.cpp", - "CursorInputMapper.cpp", - "CursorScrollAccumulator.cpp", "EventHub.cpp", - "ExternalStylusInputMapper.cpp", "InputDevice.cpp", - "InputMapper.cpp", + "mapper/accumulator/CursorButtonAccumulator.cpp", + "mapper/accumulator/CursorScrollAccumulator.cpp", + "mapper/accumulator/SingleTouchMotionAccumulator.cpp", + "mapper/accumulator/TouchButtonAccumulator.cpp", + "mapper/CursorInputMapper.cpp", + "mapper/ExternalStylusInputMapper.cpp", + "mapper/InputMapper.cpp", + "mapper/JoystickInputMapper.cpp", + "mapper/KeyboardInputMapper.cpp", + "mapper/MultiTouchInputMapper.cpp", + "mapper/RotaryEncoderInputMapper.cpp", + "mapper/SingleTouchInputMapper.cpp", + "mapper/SwitchInputMapper.cpp", + "mapper/TouchInputMapper.cpp", + "mapper/VibratorInputMapper.cpp", "InputReader.cpp", "InputReaderFactory.cpp", - "JoystickInputMapper.cpp", - "KeyboardInputMapper.cpp", - "MultiTouchInputMapper.cpp", - "RotaryEncoderInputMapper.cpp", - "SingleTouchInputMapper.cpp", - "SingleTouchMotionAccumulator.cpp", - "SwitchInputMapper.cpp", - "TouchButtonAccumulator.cpp", - "TouchInputMapper.cpp", "TouchVideoDevice.cpp", - "VibratorInputMapper.cpp", ], shared_libs: [ diff --git a/services/inputflinger/reader/CursorButtonAccumulator.cpp b/services/inputflinger/reader/CursorButtonAccumulator.cpp deleted file mode 100644 index 0337d51126..0000000000 --- a/services/inputflinger/reader/CursorButtonAccumulator.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CursorButtonAccumulator.h" - -#include "EventHub.h" -#include "InputDevice.h" - -namespace android { - -CursorButtonAccumulator::CursorButtonAccumulator() { - clearButtons(); -} - -void CursorButtonAccumulator::reset(InputDevice* device) { - mBtnLeft = device->isKeyPressed(BTN_LEFT); - mBtnRight = device->isKeyPressed(BTN_RIGHT); - mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); - mBtnBack = device->isKeyPressed(BTN_BACK); - mBtnSide = device->isKeyPressed(BTN_SIDE); - mBtnForward = device->isKeyPressed(BTN_FORWARD); - mBtnExtra = device->isKeyPressed(BTN_EXTRA); - mBtnTask = device->isKeyPressed(BTN_TASK); -} - -void CursorButtonAccumulator::clearButtons() { - mBtnLeft = 0; - mBtnRight = 0; - mBtnMiddle = 0; - mBtnBack = 0; - mBtnSide = 0; - mBtnForward = 0; - mBtnExtra = 0; - mBtnTask = 0; -} - -void CursorButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_LEFT: - mBtnLeft = rawEvent->value; - break; - case BTN_RIGHT: - mBtnRight = rawEvent->value; - break; - case BTN_MIDDLE: - mBtnMiddle = rawEvent->value; - break; - case BTN_BACK: - mBtnBack = rawEvent->value; - break; - case BTN_SIDE: - mBtnSide = rawEvent->value; - break; - case BTN_FORWARD: - mBtnForward = rawEvent->value; - break; - case BTN_EXTRA: - mBtnExtra = rawEvent->value; - break; - case BTN_TASK: - mBtnTask = rawEvent->value; - break; - } - } -} - -uint32_t CursorButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnLeft) { - result |= AMOTION_EVENT_BUTTON_PRIMARY; - } - if (mBtnRight) { - result |= AMOTION_EVENT_BUTTON_SECONDARY; - } - if (mBtnMiddle) { - result |= AMOTION_EVENT_BUTTON_TERTIARY; - } - if (mBtnBack || mBtnSide) { - result |= AMOTION_EVENT_BUTTON_BACK; - } - if (mBtnForward || mBtnExtra) { - result |= AMOTION_EVENT_BUTTON_FORWARD; - } - return result; -} - -} // namespace android diff --git a/services/inputflinger/reader/CursorInputMapper.cpp b/services/inputflinger/reader/CursorInputMapper.cpp deleted file mode 100644 index f69138ea09..0000000000 --- a/services/inputflinger/reader/CursorInputMapper.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "CursorInputMapper.h" - -#include "CursorButtonAccumulator.h" -#include "CursorScrollAccumulator.h" -#include "TouchCursorInputMapperCommon.h" - -namespace android { - -// --- CursorMotionAccumulator --- - -CursorMotionAccumulator::CursorMotionAccumulator() { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::clearRelativeAxes() { - mRelX = 0; - mRelY = 0; -} - -void CursorMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_X: - mRelX = rawEvent->value; - break; - case REL_Y: - mRelY = rawEvent->value; - break; - } - } -} - -void CursorMotionAccumulator::finishSync() { - clearRelativeAxes(); -} - -// --- CursorInputMapper --- - -CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} - -CursorInputMapper::~CursorInputMapper() {} - -uint32_t CursorInputMapper::getSources() { - return mSource; -} - -void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mParameters.mode == Parameters::MODE_POINTER) { - float minX, minY, maxX, maxY; - if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); - } - } else { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); - } - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } -} - -void CursorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Cursor Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeVWheel())); - dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeHWheel())); - dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); - dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); - dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); - dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - mCursorScrollAccumulator.configure(getDevice()); - - // Configure basic parameters. - configureParameters(); - - // Configure device mode. - switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: - // Should not happen during first time configuration. - ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; - [[fallthrough]]; - case Parameters::MODE_POINTER: - mSource = AINPUT_SOURCE_MOUSE; - mXPrecision = 1.0f; - mYPrecision = 1.0f; - mXScale = 1.0f; - mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - break; - case Parameters::MODE_NAVIGATION: - mSource = AINPUT_SOURCE_TRACKBALL; - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - break; - } - - mVWheelScale = 1.0f; - mHWheelScale = 1.0f; - } - - if ((!changes && config->pointerCapture) || - (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { - if (config->pointerCapture) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; - mSource = AINPUT_SOURCE_MOUSE_RELATIVE; - // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else { - ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); - } - } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; - mSource = AINPUT_SOURCE_MOUSE; - } else { - ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); - } - } - bumpGeneration(); - if (changes) { - getDevice()->notifyReset(when); - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mOrientation = DISPLAY_ORIENTATION_0; - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } - } - - // Update the PointerController if viewports changed. - if (mParameters.mode == Parameters::MODE_POINTER) { - getPolicy()->obtainPointerController(getDeviceId()); - } - bumpGeneration(); - } -} - -void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; - String8 cursorModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { - if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; - } else if (cursorModeString != "pointer" && cursorModeString != "default") { - ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); - } - } - - mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { - mParameters.hasAssociatedDisplay = true; - } -} - -void CursorInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", - toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); -} - -void CursorInputMapper::reset(nsecs_t when) { - mButtonState = 0; - mDownTime = 0; - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mCursorButtonAccumulator.reset(getDevice()); - mCursorMotionAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void CursorInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorMotionAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void CursorInputMapper::sync(nsecs_t when) { - int32_t lastButtonState = mButtonState; - int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); - mButtonState = currentButtonState; - - bool wasDown = isPointerDown(lastButtonState); - bool down = isPointerDown(currentButtonState); - bool downChanged; - if (!wasDown && down) { - mDownTime = when; - downChanged = true; - } else if (wasDown && !down) { - downChanged = true; - } else { - downChanged = false; - } - nsecs_t downTime = mDownTime; - bool buttonsChanged = currentButtonState != lastButtonState; - int32_t buttonsPressed = currentButtonState & ~lastButtonState; - int32_t buttonsReleased = lastButtonState & ~currentButtonState; - - float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; - float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; - bool moved = deltaX != 0 || deltaY != 0; - - // Rotate delta according to orientation if needed. - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && - (deltaX != 0.0f || deltaY != 0.0f)) { - rotateDelta(mOrientation, &deltaX, &deltaY); - } - - // Move the pointer. - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); - float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); - bool scrolled = vscroll != 0 || hscroll != 0; - - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - int32_t displayId; - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); - } - - if (buttonsChanged) { - mPointerController->setButtonState(currentButtonState); - } - - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = mPointerController->getDisplayId(); - } else { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); - displayId = ADISPLAY_ID_NONE; - } - - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - - // Moving an external trackball or mouse should wake the device. - // We don't do this for internal cursor devices to prevent them from waking up - // the device in your pocket. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - // Synthesize key down from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - // Send motion event. - if (downChanged || moved || scrolled || buttonsChanged) { - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = lastButtonState; - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } else { - motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; - } - - if (buttonsReleased) { - BitSet32 released(buttonsReleased); - while (!released.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); - buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); - } - } - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - - if (buttonsPressed) { - BitSet32 pressed(buttonsPressed); - while (!pressed.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); - buttonState |= actionButton; - NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); - } - } - - ALOG_ASSERT(buttonState == currentButtonState); - - // Send hover move after UP to tell the application that the mouse is hovering now. - if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); - } - - // Send scroll events. - if (scrolled) { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - } - - // Synthesize key up from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - mCursorMotionAccumulator.finishSync(); - mCursorScrollAccumulator.finishSync(); -} - -int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); - } else { - return AKEY_STATE_UNKNOWN; - } -} - -void CursorInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -std::optional CursorInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - // If the device is orientationAware and not a mouse, - // it expects to dispatch events to any display - return std::make_optional(ADISPLAY_ID_NONE); - } - } - return std::nullopt; -} - -} // namespace android diff --git a/services/inputflinger/reader/CursorScrollAccumulator.cpp b/services/inputflinger/reader/CursorScrollAccumulator.cpp deleted file mode 100644 index d744096d94..0000000000 --- a/services/inputflinger/reader/CursorScrollAccumulator.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CursorScrollAccumulator.h" - -#include "EventHub.h" -#include "InputDevice.h" - -namespace android { - -CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::configure(InputDevice* device) { - mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); - mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); -} - -void CursorScrollAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::clearRelativeAxes() { - mRelWheel = 0; - mRelHWheel = 0; -} - -void CursorScrollAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_WHEEL: - mRelWheel = rawEvent->value; - break; - case REL_HWHEEL: - mRelHWheel = rawEvent->value; - break; - } - } -} - -void CursorScrollAccumulator::finishSync() { - clearRelativeAxes(); -} - -} // namespace android diff --git a/services/inputflinger/reader/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/ExternalStylusInputMapper.cpp deleted file mode 100644 index 9aa0770245..0000000000 --- a/services/inputflinger/reader/ExternalStylusInputMapper.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "ExternalStylusInputMapper.h" - -#include "SingleTouchMotionAccumulator.h" -#include "TouchButtonAccumulator.h" - -namespace android { - -ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} - -uint32_t ExternalStylusInputMapper::getSources() { - return AINPUT_SOURCE_STYLUS; -} - -void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f); -} - -void ExternalStylusInputMapper::dump(std::string& dump) { - dump += INDENT2 "External Stylus Input Mapper:\n"; - dump += INDENT3 "Raw Stylus Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); - dump += INDENT3 "Stylus State:\n"; - dumpStylusState(dump, mStylusState); -} - -void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDevice()); -} - -void ExternalStylusInputMapper::reset(nsecs_t when) { - InputDevice* device = getDevice(); - mSingleTouchMotionAccumulator.reset(device); - mTouchButtonAccumulator.reset(device); - InputMapper::reset(when); -} - -void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { - mSingleTouchMotionAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void ExternalStylusInputMapper::sync(nsecs_t when) { - mStylusState.clear(); - - mStylusState.when = when; - - mStylusState.toolType = mTouchButtonAccumulator.getToolType(); - if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; - } - - mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - - mContext->dispatchExternalStylusState(mStylusState); -} - -} // namespace android diff --git a/services/inputflinger/reader/ExternalStylusInputMapper.h b/services/inputflinger/reader/ExternalStylusInputMapper.h deleted file mode 100644 index 9764fbb3c1..0000000000 --- a/services/inputflinger/reader/ExternalStylusInputMapper.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H -#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H - -#include "InputMapper.h" - -#include "SingleTouchMotionAccumulator.h" -#include "StylusState.h" -#include "TouchButtonAccumulator.h" - -namespace android { - -class ExternalStylusInputMapper : public InputMapper { -public: - explicit ExternalStylusInputMapper(InputDevice* device); - virtual ~ExternalStylusInputMapper() = default; - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; - RawAbsoluteAxisInfo mRawPressureAxis; - TouchButtonAccumulator mTouchButtonAccumulator; - - StylusState mStylusState; -}; - -} // namespace android - -#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/InputMapper.cpp b/services/inputflinger/reader/InputMapper.cpp deleted file mode 100644 index d941528d14..0000000000 --- a/services/inputflinger/reader/InputMapper.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "InputMapper.h" - -#include "InputDevice.h" - -namespace android { - -InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} - -InputMapper::~InputMapper() {} - -void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { - info->addSource(getSources()); -} - -void InputMapper::dump(std::string& dump) {} - -void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) {} - -void InputMapper::reset(nsecs_t when) {} - -void InputMapper::timeoutExpired(nsecs_t when) {} - -int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return AKEY_STATE_UNKNOWN; -} - -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return false; -} - -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) {} - -void InputMapper::cancelVibrate(int32_t token) {} - -void InputMapper::cancelTouch(nsecs_t when) {} - -int32_t InputMapper::getMetaState() { - return 0; -} - -void InputMapper::updateMetaState(int32_t keyCode) {} - -void InputMapper::updateExternalStylusState(const StylusState& state) {} - -void InputMapper::fadePointer() {} - -status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); -} - -void InputMapper::bumpGeneration() { - mDevice->bumpGeneration(); -} - -void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, - const char* name) { - if (axis.valid) { - dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, - axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); - } else { - dump += StringPrintf(INDENT4 "%s: unknown range\n", name); - } -} - -void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { - dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); - dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); - dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); -} - -} // namespace android diff --git a/services/inputflinger/reader/JoystickInputMapper.cpp b/services/inputflinger/reader/JoystickInputMapper.cpp deleted file mode 100644 index 50adf73c76..0000000000 --- a/services/inputflinger/reader/JoystickInputMapper.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "JoystickInputMapper.h" - -namespace android { - -JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} - -JoystickInputMapper::~JoystickInputMapper() {} - -uint32_t JoystickInputMapper::getSources() { - return AINPUT_SOURCE_JOYSTICK; -} - -void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - for (size_t i = 0; i < mAxes.size(); i++) { - const Axis& axis = mAxes.valueAt(i); - addMotionRange(axis.axisInfo.axis, axis, info); - - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - addMotionRange(axis.axisInfo.highAxis, axis, info); - } - } -} - -void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { - info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, - axis.resolution); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to register - * the old axes as duplicates of their corresponding new ones. */ - int32_t compatAxis = getCompatAxis(axisId); - if (compatAxis >= 0) { - info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, - axis.fuzz, axis.resolution); - } -} - -/* A mapping from axes the joystick actually has to the axes that should be - * artificially created for compatibility purposes. - * Returns -1 if no compatibility axis is needed. */ -int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_LTRIGGER: - return AMOTION_EVENT_AXIS_BRAKE; - case AMOTION_EVENT_AXIS_RTRIGGER: - return AMOTION_EVENT_AXIS_GAS; - } - return -1; -} - -void JoystickInputMapper::dump(std::string& dump) { - dump += INDENT2 "Joystick Input Mapper:\n"; - - dump += INDENT3 "Axes:\n"; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - const char* label = getAxisLabel(axis.axisInfo.axis); - if (label) { - dump += StringPrintf(INDENT4 "%s", label); - } else { - dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - label = getAxisLabel(axis.axisInfo.highAxis); - if (label) { - dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); - } else { - dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, - axis.axisInfo.splitValue); - } - } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { - dump += " (invert)"; - } - - dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " - "highScale=%0.5f, highOffset=%0.5f\n", - axis.scale, axis.offset, axis.highScale, axis.highOffset); - dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " - "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", - mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, - axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, - axis.rawAxisInfo.resolution); - } -} - -void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Collect all axes. - for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { - continue; // axis must be claimed by a different device - } - - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { - // Map axis. - AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); - if (!explicitlyMapped) { - // Axis is not explicitly mapped, will choose a generic axis later. - axisInfo.mode = AxisInfo::MODE_NORMAL; - axisInfo.axis = -1; - } - - // Apply flat override. - int32_t rawFlat = - axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, - offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } - - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; - - mAxes.add(abs, axis); - } - } - - // If there are too many axes, start dropping them. - // Prefer to keep explicitly mapped axes. - if (mAxes.size() > PointerCoords::MAX_AXES) { - ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", - getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); - pruneAxes(true); - pruneAxes(false); - } - - // Assign generic axis ids to remaining axes. - int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && - haveAxis(nextGenericAxisId)) { - nextGenericAxisId += 1; - } - - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { - axis.axisInfo.axis = nextGenericAxisId; - nextGenericAxisId += 1; - } else { - ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " - "have already been assigned to other axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; - } - } - } - } -} - -bool JoystickInputMapper::haveAxis(int32_t axisId) { - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - if (axis.axisInfo.axis == axisId || - (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { - return true; - } - } - return false; -} - -void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { - size_t i = mAxes.size(); - while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { - if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { - continue; - } - ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i); - } -} - -bool JoystickInputMapper::isCenteredAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_X: - case AMOTION_EVENT_AXIS_Y: - case AMOTION_EVENT_AXIS_Z: - case AMOTION_EVENT_AXIS_RX: - case AMOTION_EVENT_AXIS_RY: - case AMOTION_EVENT_AXIS_RZ: - case AMOTION_EVENT_AXIS_HAT_X: - case AMOTION_EVENT_AXIS_HAT_Y: - case AMOTION_EVENT_AXIS_ORIENTATION: - case AMOTION_EVENT_AXIS_RUDDER: - case AMOTION_EVENT_AXIS_WHEEL: - return true; - default: - return false; - } -} - -void JoystickInputMapper::reset(nsecs_t when) { - // Recenter all axes. - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - axis.resetValue(); - } - - InputMapper::reset(when); -} - -void JoystickInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->code); - if (index >= 0) { - Axis& axis = mAxes.editValueAt(index); - float newValue, highNewValue; - switch (axis.axisInfo.mode) { - case AxisInfo::MODE_INVERT: - newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + - axis.offset; - highNewValue = 0.0f; - break; - case AxisInfo::MODE_SPLIT: - if (rawEvent->value < axis.axisInfo.splitValue) { - newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + - axis.offset; - highNewValue = 0.0f; - } else if (rawEvent->value > axis.axisInfo.splitValue) { - newValue = 0.0f; - highNewValue = - (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + - axis.highOffset; - } else { - newValue = 0.0f; - highNewValue = 0.0f; - } - break; - default: - newValue = rawEvent->value * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - } - axis.newValue = newValue; - axis.highNewValue = highNewValue; - } - break; - } - - case EV_SYN: - switch (rawEvent->code) { - case SYN_REPORT: - sync(rawEvent->when, false /*force*/); - break; - } - break; - } -} - -void JoystickInputMapper::sync(nsecs_t when, bool force) { - if (!filterAxes(force)) { - return; - } - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = 0; - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, - axis.highCurrentValue); - } - } - - // Moving a joystick axis should not wake the device because joysticks can - // be fairly noisy even when not in use. On the other hand, pushing a gamepad - // button will likely wake the device. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); -} - -void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, - float value) { - pointerCoords->setAxisValue(axis, value); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to produce - * values for the old axes as mirrors of the value of their corresponding - * new axes. */ - int32_t compatAxis = getCompatAxis(axis); - if (compatAxis >= 0) { - pointerCoords->setAxisValue(compatAxis, value); - } -} - -bool JoystickInputMapper::filterAxes(bool force) { - bool atLeastOneSignificantChange = force; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (force || - hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, - axis.max)) { - axis.currentValue = axis.newValue; - atLeastOneSignificantChange = true; - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - if (force || - hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, - axis.min, axis.max)) { - axis.highCurrentValue = axis.highNewValue; - atLeastOneSignificantChange = true; - } - } - } - return atLeastOneSignificantChange; -} - -bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, - float currentValue, float min, float max) { - if (newValue != currentValue) { - // Filter out small changes in value unless the value is converging on the axis - // bounds or center point. This is intended to reduce the amount of information - // sent to applications by particularly noisy joysticks (such as PS3). - if (fabs(newValue - currentValue) > filter || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || - hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { - return true; - } - } - return false; -} - -bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, - float currentValue, - float thresholdValue) { - float newDistance = fabs(newValue - thresholdValue); - if (newDistance < filter) { - float oldDistance = fabs(currentValue - thresholdValue); - if (newDistance < oldDistance) { - return true; - } - } - return false; -} - -} // namespace android diff --git a/services/inputflinger/reader/JoystickInputMapper.h b/services/inputflinger/reader/JoystickInputMapper.h deleted file mode 100644 index 1b071d0480..0000000000 --- a/services/inputflinger/reader/JoystickInputMapper.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H -#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H - -#include "InputMapper.h" - -namespace android { - -class JoystickInputMapper : public InputMapper { -public: - explicit JoystickInputMapper(InputDevice* device); - virtual ~JoystickInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - struct Axis { - RawAbsoluteAxisInfo rawAxisInfo; - AxisInfo axisInfo; - - bool explicitlyMapped; // true if the axis was explicitly assigned an axis id - - float scale; // scale factor from raw to normalized values - float offset; // offset to add after scaling for normalization - float highScale; // scale factor from raw to normalized values of high split - float highOffset; // offset to add after scaling for normalization of high split - - float min; // normalized inclusive minimum - float max; // normalized inclusive maximum - float flat; // normalized flat region size - float fuzz; // normalized error tolerance - float resolution; // normalized resolution in units/mm - - float filter; // filter out small variations of this size - float currentValue; // current value - float newValue; // most recent value - float highCurrentValue; // current value of high split - float highNewValue; // most recent value of high split - - void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, - bool explicitlyMapped, float scale, float offset, float highScale, - float highOffset, float min, float max, float flat, float fuzz, - float resolution) { - this->rawAxisInfo = rawAxisInfo; - this->axisInfo = axisInfo; - this->explicitlyMapped = explicitlyMapped; - this->scale = scale; - this->offset = offset; - this->highScale = highScale; - this->highOffset = highOffset; - this->min = min; - this->max = max; - this->flat = flat; - this->fuzz = fuzz; - this->resolution = resolution; - this->filter = 0; - resetValue(); - } - - void resetValue() { - this->currentValue = 0; - this->newValue = 0; - this->highCurrentValue = 0; - this->highNewValue = 0; - } - }; - - // Axes indexed by raw ABS_* axis index. - KeyedVector mAxes; - - void sync(nsecs_t when, bool force); - - bool haveAxis(int32_t axisId); - void pruneAxes(bool ignoreExplicitlyMappedAxes); - bool filterAxes(bool force); - - static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, - float min, float max); - static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, - float currentValue, float thresholdValue); - - static bool isCenteredAxis(int32_t axis); - static int32_t getCompatAxis(int32_t axis); - - static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); - static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/KeyboardInputMapper.cpp b/services/inputflinger/reader/KeyboardInputMapper.cpp deleted file mode 100644 index f51d4a0d4a..0000000000 --- a/services/inputflinger/reader/KeyboardInputMapper.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "KeyboardInputMapper.h" - -namespace android { - -// --- Static Definitions --- - -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { - if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; - } - } - } - return value; -} - -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, - {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, - {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, - {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, - {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, - {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, - {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, - {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, -}; - -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], - size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; -} - -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, - {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, - {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, - {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, -}; - -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, - keyCodeRotationMapSize); -} - -// --- KeyboardInputMapper --- - -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) - : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} - -KeyboardInputMapper::~KeyboardInputMapper() {} - -uint32_t KeyboardInputMapper::getSources() { - return mSource; -} - -int32_t KeyboardInputMapper::getOrientation() { - if (mViewport) { - return mViewport->orientation; - } - return DISPLAY_ORIENTATION_0; -} - -int32_t KeyboardInputMapper::getDisplayId() { - if (mViewport) { - return mViewport->displayId; - } - return ADISPLAY_ID_NONE; -} - -void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); -} - -void KeyboardInputMapper::dump(std::string& dump) { - dump += INDENT2 "Keyboard Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); - dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); - dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -std::optional KeyboardInputMapper::findViewport( - nsecs_t when, const InputReaderConfiguration* config) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // No associated display defined, try to find default display if orientationAware. - if (mParameters.orientationAware) { - return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return std::nullopt; -} - -void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = findViewport(when, config); - } -} - -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { - int32_t mapped = 0; - if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } - } -} - -void KeyboardInputMapper::configureParameters() { - mParameters.orientationAware = false; - const PropertyMap& config = getDevice()->getConfiguration(); - config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } - - mParameters.handlesKeyRepeat = false; - config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); -} - -void KeyboardInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); - dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); -} - -void KeyboardInputMapper::reset(nsecs_t when) { - mMetaState = AMETA_NONE; - mDownTime = 0; - mKeyDowns.clear(); - mCurrentHidUsage = 0; - - resetLedState(); - - InputMapper::reset(when); -} - -void KeyboardInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); - } - break; - } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; - } - break; - } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } - } -} - -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE || scanCode >= KEY_OK || - (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || - (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { - int32_t keyCode; - int32_t keyMetaState; - uint32_t policyFlags; - - if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, - &keyMetaState, &policyFlags)) { - keyCode = AKEYCODE_UNKNOWN; - keyMetaState = mMetaState; - policyFlags = 0; - } - - if (down) { - // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware) { - keyCode = rotateKeyCode(keyCode, getOrientation()); - } - - // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) && - mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { - return; - } - if (policyFlags & POLICY_FLAG_GESTURE) { - mDevice->cancelTouch(when); - } - - KeyDown keyDown; - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; - mKeyDowns.push_back(keyDown); - } - - mDownTime = when; - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); - } else { - // key was not actually down - ALOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().c_str(), keyCode, scanCode); - return; - } - } - - if (updateMetaStateIfNeeded(keyCode, down)) { - // If global meta state changed send it along with the key. - // If it has not changed then we'll use what keymap gave us, - // since key replacement logic might temporarily reset a few - // meta bits for given key. - keyMetaState = mMetaState; - } - - nsecs_t downTime = mDownTime; - - // Key down on external an keyboard should wake the device. - // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. - // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mParameters.handlesKeyRepeat) { - policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; - } - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), - policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); -} - -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { - size_t n = mKeyDowns.size(); - for (size_t i = 0; i < n; i++) { - if (mKeyDowns[i].scanCode == scanCode) { - return i; - } - } - return -1; -} - -int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); -} - -int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); -} - -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); -} - -int32_t KeyboardInputMapper::getMetaState() { - return mMetaState; -} - -void KeyboardInputMapper::updateMetaState(int32_t keyCode) { - updateMetaStateIfNeeded(keyCode, false); -} - -bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { - int32_t oldMetaState = mMetaState; - int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); - bool metaStateChanged = oldMetaState != newMetaState; - if (metaStateChanged) { - mMetaState = newMetaState; - updateLedState(false); - - getContext()->updateGlobalMetaState(); - } - - return metaStateChanged; -} - -void KeyboardInputMapper::resetLedState() { - initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); - initializeLedState(mNumLockLedState, ALED_NUM_LOCK); - initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); - - updateLedState(true); -} - -void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { - ledState.avail = getEventHub()->hasLed(getDeviceId(), led); - ledState.on = false; -} - -void KeyboardInputMapper::updateLedState(bool reset) { - updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); - updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); - updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); -} - -void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, - int32_t modifier, bool reset) { - if (ledState.avail) { - bool desiredState = (mMetaState & modifier) != 0; - if (reset || ledState.on != desiredState) { - getEventHub()->setLedState(getDeviceId(), led, desiredState); - ledState.on = desiredState; - } - } -} - -std::optional KeyboardInputMapper::getAssociatedDisplayId() { - if (mViewport) { - return std::make_optional(mViewport->displayId); - } - return std::nullopt; -} - -} // namespace android diff --git a/services/inputflinger/reader/MultiTouchInputMapper.cpp b/services/inputflinger/reader/MultiTouchInputMapper.cpp deleted file mode 100644 index c567c8bf80..0000000000 --- a/services/inputflinger/reader/MultiTouchInputMapper.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "MultiTouchInputMapper.h" - -namespace android { - -// --- Constants --- - -// Maximum number of slots supported when using the slot-based Multitouch Protocol B. -static constexpr size_t MAX_SLOTS = 32; - -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mSlots(nullptr), - mSlotCount(0), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { - delete[] mSlots; -} - -void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, - bool usingSlotsProtocol) { - mSlotCount = slotCount; - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - - delete[] mSlots; - mSlots = new Slot[slotCount]; -} - -void MultiTouchMotionAccumulator::reset(InputDevice* device) { - // Unfortunately there is no way to read the initial contents of the slots. - // So when we reset the accumulator, we must assume they are all zeroes. - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, - &initialSlot); - if (status) { - ALOGD("Could not retrieve current multitouch slot index. status=%d", status); - initialSlot = -1; - } - clearSlots(initialSlot); - } else { - clearSlots(-1); - } -} - -void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { - if (mSlots) { - for (size_t i = 0; i < mSlotCount; i++) { - mSlots[i].clear(); - } - } - mCurrentSlot = initialSlot; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); - } -#endif - } else { - Slot* slot = &mSlots[mCurrentSlot]; - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot->mInUse = true; - slot->mAbsMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - slot->mInUse = true; - slot->mAbsMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - slot->mInUse = true; - slot->mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->mInUse = true; - slot->mAbsMTTouchMinor = rawEvent->value; - slot->mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot->mInUse = true; - slot->mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->mInUse = true; - slot->mAbsMTWidthMinor = rawEvent->value; - slot->mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot->mInUse = true; - slot->mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot->mInUse = false; - } else { - slot->mInUse = true; - slot->mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->mInUse = true; - slot->mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot->mInUse = true; - slot->mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->mInUse = true; - slot->mAbsMTToolType = rawEvent->value; - slot->mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - clearSlots(-1); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - -// --- MultiTouchMotionAccumulator::Slot --- - -MultiTouchMotionAccumulator::Slot::Slot() { - clear(); -} - -void MultiTouchMotionAccumulator::Slot::clear() { - mInUse = false; - mHaveAbsMTTouchMinor = false; - mHaveAbsMTWidthMinor = false; - mHaveAbsMTToolType = false; - mAbsMTPositionX = 0; - mAbsMTPositionY = 0; - mAbsMTTouchMajor = 0; - mAbsMTTouchMinor = 0; - mAbsMTWidthMajor = 0; - mAbsMTWidthMinor = 0; - mAbsMTOrientation = 0; - mAbsMTTrackingId = -1; - mAbsMTPressure = 0; - mAbsMTDistance = 0; - mAbsMTToolType = 0; -} - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -// --- MultiTouchInputMapper --- - -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} - -MultiTouchInputMapper::~MultiTouchInputMapper() {} - -void MultiTouchInputMapper::reset(nsecs_t when) { - mMultiTouchMotionAccumulator.reset(getDevice()); - - mPointerIdBits.clear(); - - TouchInputMapper::reset(when); -} - -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mMultiTouchMotionAccumulator.process(rawEvent); -} - -void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); - size_t outCount = 0; - BitSet32 newPointerIdBits; - mHavePointerIds = true; - - for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const MultiTouchMotionAccumulator::Slot* inSlot = - mMultiTouchMotionAccumulator.getSlot(inIndex); - if (!inSlot->isInUse()) { - continue; - } - - if (outCount >= MAX_POINTERS) { -#if DEBUG_POINTERS - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); -#endif - break; // too many fingers! - } - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; - outPointer.x = inSlot->getX(); - outPointer.y = inSlot->getY(); - outPointer.pressure = inSlot->getPressure(); - outPointer.touchMajor = inSlot->getTouchMajor(); - outPointer.touchMinor = inSlot->getTouchMinor(); - outPointer.toolMajor = inSlot->getToolMajor(); - outPointer.toolMinor = inSlot->getToolMinor(); - outPointer.orientation = inSlot->getOrientation(); - outPointer.distance = inSlot->getDistance(); - outPointer.tiltX = 0; - outPointer.tiltY = 0; - - outPointer.toolType = inSlot->getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - } - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && - (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); - outPointer.isHovering = isHovering; - - // Assign pointer id using tracking id if available. - if (mHavePointerIds) { - int32_t trackingId = inSlot->getTrackingId(); - int32_t id = -1; - if (trackingId >= 0) { - for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { - uint32_t n = idBits.clearFirstMarkedBit(); - if (mPointerTrackingIdMap[n] == trackingId) { - id = n; - } - } - - if (id < 0 && !mPointerIdBits.isFull()) { - id = mPointerIdBits.markFirstUnmarkedBit(); - mPointerTrackingIdMap[id] = trackingId; - } - } - if (id < 0) { - mHavePointerIds = false; - outState->rawPointerData.clearIdBits(); - newPointerIdBits.clear(); - } else { - outPointer.id = id; - outState->rawPointerData.idToIndex[id] = outCount; - outState->rawPointerData.markIdBit(id, isHovering); - newPointerIdBits.markBit(id); - } - } - outCount += 1; - } - - outState->rawPointerData.pointerCount = outCount; - mPointerIdBits = newPointerIdBits; - - mMultiTouchMotionAccumulator.finishSync(); -} - -void MultiTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); - getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); - getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); - getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); - - if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && - mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { - size_t slotCount = mRawPointerAxes.slot.maxValue + 1; - if (slotCount > MAX_SLOTS) { - ALOGW("MultiTouch Device %s reported %zu slots but the framework " - "only supports a maximum of %zu slots at this time.", - getDeviceName().c_str(), slotCount, MAX_SLOTS); - slotCount = MAX_SLOTS; - } - mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); - } else { - mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, - false /*usingSlotsProtocol*/); - } -} - -bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); -} - -} // namespace android diff --git a/services/inputflinger/reader/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/RotaryEncoderInputMapper.cpp deleted file mode 100644 index e113ccad84..0000000000 --- a/services/inputflinger/reader/RotaryEncoderInputMapper.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "RotaryEncoderInputMapper.h" - -#include "CursorScrollAccumulator.h" - -namespace android { - -RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) - : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { - mSource = AINPUT_SOURCE_ROTARY_ENCODER; -} - -RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} - -uint32_t RotaryEncoderInputMapper::getSources() { - return mSource; -} - -void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { - float res = 0.0f; - if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { - ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); - } - if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), - mScalingFactor)) { - ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," - "default to 1.0!\n"); - mScalingFactor = 1.0f; - } - info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - res * mScalingFactor); - } -} - -void RotaryEncoderInputMapper::dump(std::string& dump) { - dump += INDENT2 "Rotary Encoder Input Mapper:\n"; - dump += StringPrintf(INDENT3 "HaveWheel: %s\n", - toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); -} - -void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - if (!changes) { - mRotaryEncoderScrollAccumulator.configure(getDevice()); - } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - std::optional internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; - } - } -} - -void RotaryEncoderInputMapper::reset(nsecs_t when) { - mRotaryEncoderScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { - mRotaryEncoderScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void RotaryEncoderInputMapper::sync(nsecs_t when) { - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); - bool scrolled = scroll != 0; - - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - - // Send motion event. - if (scrolled) { - int32_t metaState = mContext->getGlobalMetaState(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - - mRotaryEncoderScrollAccumulator.finishSync(); -} - -} // namespace android diff --git a/services/inputflinger/reader/RotaryEncoderInputMapper.h b/services/inputflinger/reader/RotaryEncoderInputMapper.h deleted file mode 100644 index 26488373bd..0000000000 --- a/services/inputflinger/reader/RotaryEncoderInputMapper.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H -#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H - -#include "CursorScrollAccumulator.h" -#include "InputMapper.h" - -namespace android { - -class RotaryEncoderInputMapper : public InputMapper { -public: - explicit RotaryEncoderInputMapper(InputDevice* device); - virtual ~RotaryEncoderInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - CursorScrollAccumulator mRotaryEncoderScrollAccumulator; - - int32_t mSource; - float mScalingFactor; - int32_t mOrientation; - - void sync(nsecs_t when); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/SingleTouchInputMapper.cpp b/services/inputflinger/reader/SingleTouchInputMapper.cpp deleted file mode 100644 index 440d282686..0000000000 --- a/services/inputflinger/reader/SingleTouchInputMapper.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SingleTouchInputMapper.h" - -namespace android { - -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} - -SingleTouchInputMapper::~SingleTouchInputMapper() {} - -void SingleTouchInputMapper::reset(nsecs_t when) { - mSingleTouchMotionAccumulator.reset(getDevice()); - - TouchInputMapper::reset(when); -} - -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mSingleTouchMotionAccumulator.process(rawEvent); -} - -void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - if (mTouchButtonAccumulator.isToolActive()) { - outState->rawPointerData.pointerCount = 1; - outState->rawPointerData.idToIndex[0] = 0; - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && - (mTouchButtonAccumulator.isHovering() || - (mRawPointerAxes.pressure.valid && - mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); - outState->rawPointerData.markIdBit(0, isHovering); - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; - outPointer.id = 0; - outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); - outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); - outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - outPointer.touchMajor = 0; - outPointer.touchMinor = 0; - outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.orientation = 0; - outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); - outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); - outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - outPointer.isHovering = isHovering; - } -} - -void SingleTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); - getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); -} - -bool SingleTouchInputMapper::hasStylus() const { - return mTouchButtonAccumulator.hasStylus(); -} - -} // namespace android diff --git a/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp deleted file mode 100644 index e9ba727a0d..0000000000 --- a/services/inputflinger/reader/SingleTouchMotionAccumulator.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SingleTouchMotionAccumulator.h" - -#include "EventHub.h" -#include "InputDevice.h" - -namespace android { - -SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { - clearAbsoluteAxes(); -} - -void SingleTouchMotionAccumulator::reset(InputDevice* device) { - mAbsX = device->getAbsoluteAxisValue(ABS_X); - mAbsY = device->getAbsoluteAxisValue(ABS_Y); - mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); -} - -void SingleTouchMotionAccumulator::clearAbsoluteAxes() { - mAbsX = 0; - mAbsY = 0; - mAbsPressure = 0; - mAbsToolWidth = 0; - mAbsDistance = 0; - mAbsTiltX = 0; - mAbsTiltY = 0; -} - -void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - switch (rawEvent->code) { - case ABS_X: - mAbsX = rawEvent->value; - break; - case ABS_Y: - mAbsY = rawEvent->value; - break; - case ABS_PRESSURE: - mAbsPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAbsToolWidth = rawEvent->value; - break; - case ABS_DISTANCE: - mAbsDistance = rawEvent->value; - break; - case ABS_TILT_X: - mAbsTiltX = rawEvent->value; - break; - case ABS_TILT_Y: - mAbsTiltY = rawEvent->value; - break; - } - } -} - -} // namespace android diff --git a/services/inputflinger/reader/SwitchInputMapper.cpp b/services/inputflinger/reader/SwitchInputMapper.cpp deleted file mode 100644 index 4ff941f5cf..0000000000 --- a/services/inputflinger/reader/SwitchInputMapper.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "SwitchInputMapper.h" - -namespace android { - -SwitchInputMapper::SwitchInputMapper(InputDevice* device) - : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} - -SwitchInputMapper::~SwitchInputMapper() {} - -uint32_t SwitchInputMapper::getSources() { - return AINPUT_SOURCE_SWITCH; -} - -void SwitchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_SW: - processSwitch(rawEvent->code, rawEvent->value); - break; - - case EV_SYN: - if (rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } - } -} - -void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { - if (switchCode >= 0 && switchCode < 32) { - if (switchValue) { - mSwitchValues |= 1 << switchCode; - } else { - mSwitchValues &= ~(1 << switchCode); - } - mUpdatedSwitchMask |= 1 << switchCode; - } -} - -void SwitchInputMapper::sync(nsecs_t when) { - if (mUpdatedSwitchMask) { - uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, - mUpdatedSwitchMask); - getListener()->notifySwitch(&args); - - mUpdatedSwitchMask = 0; - } -} - -int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); -} - -void SwitchInputMapper::dump(std::string& dump) { - dump += INDENT2 "Switch Input Mapper:\n"; - dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); -} - -} // namespace android diff --git a/services/inputflinger/reader/TouchButtonAccumulator.cpp b/services/inputflinger/reader/TouchButtonAccumulator.cpp deleted file mode 100644 index d2f06c86fd..0000000000 --- a/services/inputflinger/reader/TouchButtonAccumulator.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "TouchButtonAccumulator.h" - -#include "EventHub.h" -#include "InputDevice.h" - -namespace android { - -TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); -} - -void TouchButtonAccumulator::configure(InputDevice* device) { - mHaveBtnTouch = device->hasKey(BTN_TOUCH); - mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || - device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || - device->hasKey(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDevice* device) { - mBtnTouch = device->isKeyPressed(BTN_TOUCH); - mBtnStylus = device->isKeyPressed(BTN_STYLUS); - // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); - mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; -} - -void TouchButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_TOUCH: - mBtnTouch = rawEvent->value; - break; - case BTN_STYLUS: - mBtnStylus = rawEvent->value; - break; - case BTN_STYLUS2: - case BTN_0: // BTN_0 is what gets mapped for the HID usage - // Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = rawEvent->value; - break; - case BTN_TOOL_FINGER: - mBtnToolFinger = rawEvent->value; - break; - case BTN_TOOL_PEN: - mBtnToolPen = rawEvent->value; - break; - case BTN_TOOL_RUBBER: - mBtnToolRubber = rawEvent->value; - break; - case BTN_TOOL_BRUSH: - mBtnToolBrush = rawEvent->value; - break; - case BTN_TOOL_PENCIL: - mBtnToolPencil = rawEvent->value; - break; - case BTN_TOOL_AIRBRUSH: - mBtnToolAirbrush = rawEvent->value; - break; - case BTN_TOOL_MOUSE: - mBtnToolMouse = rawEvent->value; - break; - case BTN_TOOL_LENS: - mBtnToolLens = rawEvent->value; - break; - case BTN_TOOL_DOUBLETAP: - mBtnToolDoubleTap = rawEvent->value; - break; - case BTN_TOOL_TRIPLETAP: - mBtnToolTripleTap = rawEvent->value; - break; - case BTN_TOOL_QUADTAP: - mBtnToolQuadTap = rawEvent->value; - break; - } - } -} - -uint32_t TouchButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnStylus) { - result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; - } - if (mBtnStylus2) { - result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; - } - return result; -} - -int32_t TouchButtonAccumulator::getToolType() const { - if (mBtnToolMouse || mBtnToolLens) { - return AMOTION_EVENT_TOOL_TYPE_MOUSE; - } - if (mBtnToolRubber) { - return AMOTION_EVENT_TOOL_TYPE_ERASER; - } - if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { - return AMOTION_EVENT_TOOL_TYPE_FINGER; - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -bool TouchButtonAccumulator::isToolActive() const { - return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || - mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || - mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; -} - -bool TouchButtonAccumulator::isHovering() const { - return mHaveBtnTouch && !mBtnTouch; -} - -bool TouchButtonAccumulator::hasStylus() const { - return mHaveStylus; -} - -} // namespace android diff --git a/services/inputflinger/reader/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/TouchCursorInputMapperCommon.h deleted file mode 100644 index efa3d6d2b2..0000000000 --- a/services/inputflinger/reader/TouchCursorInputMapperCommon.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H -#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H - -#include "EventHub.h" -#include "InputListener.h" -#include "InputReaderContext.h" - -#include - -namespace android { - -// --- Static Definitions --- - -static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { - float temp; - switch (orientation) { - case DISPLAY_ORIENTATION_90: - temp = *deltaX; - *deltaX = *deltaY; - *deltaY = -temp; - break; - - case DISPLAY_ORIENTATION_180: - *deltaX = -*deltaX; - *deltaY = -*deltaY; - break; - - case DISPLAY_ORIENTATION_270: - temp = *deltaX; - *deltaX = -*deltaY; - *deltaY = temp; - break; - } -} - -// Returns true if the pointer should be reported as being down given the specified -// button states. This determines whether the event is reported as a touch event. -static bool isPointerDown(int32_t buttonState) { - return buttonState & - (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | - AMOTION_EVENT_BUTTON_TERTIARY); -} - -static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, - int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { - if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && - (currentButtonState & buttonState)) || - (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && - !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); - } -} - -static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, - int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, - int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, - AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, - AKEYCODE_FORWARD); -} - -} // namespace android - -#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H diff --git a/services/inputflinger/reader/TouchInputMapper.cpp b/services/inputflinger/reader/TouchInputMapper.cpp deleted file mode 100644 index 6bd0ea959c..0000000000 --- a/services/inputflinger/reader/TouchInputMapper.cpp +++ /dev/null @@ -1,3883 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "TouchInputMapper.h" - -#include "CursorButtonAccumulator.h" -#include "CursorScrollAccumulator.h" -#include "TouchButtonAccumulator.h" -#include "TouchCursorInputMapperCommon.h" - -namespace android { - -// --- Constants --- - -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - -// Artificial latency on synthetic events created from stylus data without corresponding touch -// data. -static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); - -// --- Static Definitions --- - -template -inline static void swap(T& a, T& b) { - T temp = a; - a = b; - b = temp; -} - -static float calculateCommonVector(float a, float b) { - if (a > 0 && b > 0) { - return a < b ? a : b; - } else if (a < 0 && b < 0) { - return a > b ? a : b; - } else { - return 0; - } -} - -inline static float distance(float x1, float y1, float x2, float y2) { - return hypotf(x1 - x2, y1 - y2); -} - -inline static int32_t signExtendNybble(int32_t value) { - return value >= 8 ? value - 16 : value; -} - -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - -// --- RawPointerData --- - -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} - -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { - float x = 0, y = 0; - uint32_t count = touchingIdBits.count(); - if (count) { - for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const Pointer& pointer = pointerForId(id); - x += pointer.x; - y += pointer.y; - } - x /= count; - y /= count; - } - *outX = x; - *outY = y; -} - -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -// --- TouchInputMapper --- - -TouchInputMapper::TouchInputMapper(InputDevice* device) - : InputMapper(device), - mSource(0), - mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceWidth(-1), - mSurfaceHeight(-1), - mSurfaceLeft(0), - mSurfaceTop(0), - mPhysicalWidth(-1), - mPhysicalHeight(-1), - mPhysicalLeft(0), - mPhysicalTop(0), - mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} - -TouchInputMapper::~TouchInputMapper() {} - -uint32_t TouchInputMapper::getSources() { - return mSource; -} - -void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mDeviceMode != DEVICE_MODE_DISABLED) { - info->addMotionRange(mOrientedRanges.x); - info->addMotionRange(mOrientedRanges.y); - info->addMotionRange(mOrientedRanges.pressure); - - if (mOrientedRanges.haveSize) { - info->addMotionRange(mOrientedRanges.size); - } - - if (mOrientedRanges.haveTouchSize) { - info->addMotionRange(mOrientedRanges.touchMajor); - info->addMotionRange(mOrientedRanges.touchMinor); - } - - if (mOrientedRanges.haveToolSize) { - info->addMotionRange(mOrientedRanges.toolMajor); - info->addMotionRange(mOrientedRanges.toolMinor); - } - - if (mOrientedRanges.haveOrientation) { - info->addMotionRange(mOrientedRanges.orientation); - } - - if (mOrientedRanges.haveDistance) { - info->addMotionRange(mOrientedRanges.distance); - } - - if (mOrientedRanges.haveTilt) { - info->addMotionRange(mOrientedRanges.tilt); - } - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - } - info->setButtonUnderPad(mParameters.hasButtonUnderPad); - } -} - -void TouchInputMapper::dump(std::string& dump) { - dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); - dumpParameters(dump); - dumpVirtualKeys(dump); - dumpRawPointerAxes(dump); - dumpCalibration(dump); - dumpAffineTransformation(dump); - dumpSurface(dump); - - dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); - dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); - dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); - dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); - dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); - dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); - dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); - dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); - dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); - dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); - dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); - dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); - dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); - - dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); - dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", - mLastRawState.rawPointerData.pointerCount); - for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { - const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " - "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " - "toolType=%d, isHovering=%s\n", - i, pointer.id, pointer.x, pointer.y, pointer.pressure, - pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, - pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, - pointer.distance, pointer.toolType, toString(pointer.isHovering)); - } - - dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", - mLastCookedState.buttonState); - dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", - mLastCookedState.cookedPointerData.pointerCount); - for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { - const PointerProperties& pointerProperties = - mLastCookedState.cookedPointerData.pointerProperties[i]; - const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " - "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " - "toolMinor=%0.3f, " - "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " - "toolType=%d, isHovering=%s\n", - i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), - pointerProperties.toolType, - toString(mLastCookedState.cookedPointerData.isHovering(i))); - } - - dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); - dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", - mExternalStylusFusionTimeout); - dump += INDENT3 "External Stylus State:\n"; - dumpStylusState(dump, mExternalStylusState); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); - dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); - dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); - dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); - dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); - dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); - } -} - -const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { - switch (deviceMode) { - case DEVICE_MODE_DISABLED: - return "disabled"; - case DEVICE_MODE_DIRECT: - return "direct"; - case DEVICE_MODE_UNSCALED: - return "unscaled"; - case DEVICE_MODE_NAVIGATION: - return "navigation"; - case DEVICE_MODE_POINTER: - return "pointer"; - } - return "unknown"; -} - -void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, - uint32_t changes) { - InputMapper::configure(when, config, changes); - - mConfig = *config; - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - - // Configure common accumulators. - mCursorScrollAccumulator.configure(getDevice()); - mTouchButtonAccumulator.configure(getDevice()); - - // Configure absolute axis information. - configureRawPointerAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { - // Update location calibration to reflect current settings - updateAffineTransformation(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - // Update pointer speed. - mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - } - - bool resetNeeded = false; - if (!changes || - (changes & - (InputReaderConfiguration::CHANGE_DISPLAY_INFO | - InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | - InputReaderConfiguration::CHANGE_SHOW_TOUCHES | - InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { - // Configure device sources, surface dimensions, orientation and - // scaling factors. - configureSurface(when, &resetNeeded); - } - - if (changes && resetNeeded) { - // Send reset, unless this is the first time the device has been configured, - // in which case the reader will call reset itself after all mappers are ready. - getDevice()->notifyReset(when); - } -} - -void TouchInputMapper::resolveExternalStylusPresence() { - std::vector devices; - mContext->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { - resetExternalStylus(); - } -} - -void TouchInputMapper::configureParameters() { - // Use the pointer presentation mode for devices that do not support distinct - // multitouch. The spot-based presentation relies on being able to accurately - // locate two or more fingers on the touch pad. - mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) - ? Parameters::GESTURE_MODE_SINGLE_TOUCH - : Parameters::GESTURE_MODE_MULTI_TOUCH; - - String8 gestureModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), - gestureModeString)) { - if (gestureModeString == "single-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; - } else if (gestureModeString == "multi-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; - } else if (gestureModeString != "default") { - ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); - } - } - - if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { - // The device is a touch screen. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { - // The device is a pointing device like a track pad. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || - getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { - // The device is a cursor device with a touch pad attached. - // By default don't use the touch pad to move the pointer. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else { - // The device is a touch pad of unknown purpose. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } - - mParameters.hasButtonUnderPad = - getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); - - String8 deviceTypeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), - deviceTypeString)) { - if (deviceTypeString == "touchScreen") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (deviceTypeString == "touchPad") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else if (deviceTypeString == "touchNavigation") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; - } else if (deviceTypeString == "pointer") { - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (deviceTypeString != "default") { - ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); - } - } - - mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; - getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - mParameters.associatedDisplayIsExternal = false; - if (mParameters.orientationAware || - mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || - mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - mParameters.hasAssociatedDisplay = true; - if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); - String8 uniqueDisplayId; - getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - uniqueDisplayId); - mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); - } - } - if (getDevice()->getAssociatedDisplayPort()) { - mParameters.hasAssociatedDisplay = true; - } - - // Initial downs on external touch devices should wake the device. - // Normally we don't do this for internal touch screens to prevent them from waking - // up in your pocket but you can enable it using the input device configuration. - mParameters.wake = getDevice()->isExternal(); - getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); -} - -void TouchInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - - switch (mParameters.gestureMode) { - case Parameters::GESTURE_MODE_SINGLE_TOUCH: - dump += INDENT4 "GestureMode: single-touch\n"; - break; - case Parameters::GESTURE_MODE_MULTI_TOUCH: - dump += INDENT4 "GestureMode: multi-touch\n"; - break; - default: - assert(false); - } - - switch (mParameters.deviceType) { - case Parameters::DEVICE_TYPE_TOUCH_SCREEN: - dump += INDENT4 "DeviceType: touchScreen\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_PAD: - dump += INDENT4 "DeviceType: touchPad\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: - dump += INDENT4 "DeviceType: touchNavigation\n"; - break; - case Parameters::DEVICE_TYPE_POINTER: - dump += INDENT4 "DeviceType: pointer\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " - "displayId='%s'\n", - toString(mParameters.hasAssociatedDisplay), - toString(mParameters.associatedDisplayIsExternal), - mParameters.uniqueDisplayId.c_str()); - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); -} - -void TouchInputMapper::configureRawPointerAxes() { - mRawPointerAxes.clear(); -} - -void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { - dump += INDENT3 "Raw Touch Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); -} - -bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; -} - -/** - * Determine which DisplayViewport to use. - * 1. If display port is specified, return the matching viewport. If matching viewport not - * found, then return. - * 2. If a device has associated display, get the matching viewport by either unique id or by - * the display type (internal or external). - * 3. Otherwise, use a non-display viewport. - */ -std::optional TouchInputMapper::findViewport() { - if (mParameters.hasAssociatedDisplay) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); - } - - // Check if uniqueDisplayId is specified in idc file. - if (!mParameters.uniqueDisplayId.empty()) { - return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); - } - - ViewportType viewportTypeToUse; - if (mParameters.associatedDisplayIsExternal) { - viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; - } else { - viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; - } - - std::optional viewport = - mConfig.getDisplayViewportByType(viewportTypeToUse); - if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { - ALOGW("Input device %s should be associated with external display, " - "fallback to internal one for the external viewport is not found.", - getDeviceName().c_str()); - viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return viewport; - } - - // No associated display, return a non-display viewport. - DisplayViewport newViewport; - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - newViewport.setNonDisplayViewport(rawWidth, rawHeight); - return std::make_optional(newViewport); -} - -void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { - int32_t oldDeviceMode = mDeviceMode; - - resolveExternalStylusPresence(); - - // Determine device mode. - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && - mConfig.pointerGesturesEnabled) { - mSource = AINPUT_SOURCE_MOUSE; - mDeviceMode = DEVICE_MODE_POINTER; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && - mParameters.hasAssociatedDisplay) { - mSource = AINPUT_SOURCE_TOUCHSCREEN; - mDeviceMode = DEVICE_MODE_DIRECT; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - if (hasExternalStylus()) { - mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { - mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; - mDeviceMode = DEVICE_MODE_NAVIGATION; - } else { - mSource = AINPUT_SOURCE_TOUCHPAD; - mDeviceMode = DEVICE_MODE_UNSCALED; - } - - // Ensure we have valid X and Y axes. - if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { - ALOGW("Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Get associated display dimensions. - std::optional newViewport = findViewport(); - if (!newViewport) { - ALOGI("Touch device '%s' could not query the properties of its associated " - "display. The device will be inoperable until the display size " - "becomes available.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - - bool viewportChanged = mViewport != *newViewport; - if (viewportChanged) { - mViewport = *newViewport; - - if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { - // Convert rotated viewport to natural surface coordinates. - int32_t naturalLogicalWidth, naturalLogicalHeight; - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; - switch (mViewport.orientation) { - case DISPLAY_ORIENTATION_90: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; - - mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - - mSurfaceOrientation = - mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; - } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mSurfaceWidth = rawWidth; - mSurfaceHeight = rawHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceOrientation = DISPLAY_ORIENTATION_0; - } - } - - // If moving between pointer modes, need to reset some state. - bool deviceModeChanged = mDeviceMode != oldDeviceMode; - if (deviceModeChanged) { - mOrientedRanges.clear(); - } - - // Create or update pointer controller if needed. - if (mDeviceMode == DEVICE_MODE_POINTER || - (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == nullptr || viewportChanged) { - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - } - } else { - mPointerController.clear(); - } - - if (viewportChanged || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " - "display id %d", - getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, - mSurfaceOrientation, mDeviceMode, mViewport.displayId); - - // Configure X and Y factors. - mXScale = float(mSurfaceWidth) / rawWidth; - mYScale = float(mSurfaceHeight) / rawHeight; - mXTranslate = -mSurfaceLeft; - mYTranslate = -mSurfaceTop; - mXPrecision = 1.0f / mXScale; - mYPrecision = 1.0f / mYScale; - - mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; - mOrientedRanges.x.source = mSource; - mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; - mOrientedRanges.y.source = mSource; - - configureVirtualKeys(); - - // Scale factor for terms that are not oriented in a particular axis. - // If the pixels are square then xScale == yScale otherwise we fake it - // by choosing an average. - mGeometricScale = avg(mXScale, mYScale); - - // Size of diagonal axis. - float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Size factors. - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; - } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; - } else { - mSizeScale = 0.0f; - } - - mOrientedRanges.haveTouchSize = true; - mOrientedRanges.haveToolSize = true; - mOrientedRanges.haveSize = true; - - mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; - mOrientedRanges.touchMajor.source = mSource; - mOrientedRanges.touchMajor.min = 0; - mOrientedRanges.touchMajor.max = diagonalSize; - mOrientedRanges.touchMajor.flat = 0; - mOrientedRanges.touchMajor.fuzz = 0; - mOrientedRanges.touchMajor.resolution = 0; - - mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; - mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; - - mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; - mOrientedRanges.toolMajor.source = mSource; - mOrientedRanges.toolMajor.min = 0; - mOrientedRanges.toolMajor.max = diagonalSize; - mOrientedRanges.toolMajor.flat = 0; - mOrientedRanges.toolMajor.fuzz = 0; - mOrientedRanges.toolMajor.resolution = 0; - - mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; - mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; - - mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; - mOrientedRanges.size.source = mSource; - mOrientedRanges.size.min = 0; - mOrientedRanges.size.max = 1.0; - mOrientedRanges.size.flat = 0; - mOrientedRanges.size.fuzz = 0; - mOrientedRanges.size.resolution = 0; - } else { - mSizeScale = 0.0f; - } - - // Pressure factors. - mPressureScale = 0; - float pressureMax = 1.0; - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || - mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { - if (mCalibration.havePressureScale) { - mPressureScale = mCalibration.pressureScale; - pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; - } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { - mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; - } - } - - mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; - mOrientedRanges.pressure.source = mSource; - mOrientedRanges.pressure.min = 0; - mOrientedRanges.pressure.max = pressureMax; - mOrientedRanges.pressure.flat = 0; - mOrientedRanges.pressure.fuzz = 0; - mOrientedRanges.pressure.resolution = 0; - - // Tilt - mTiltXCenter = 0; - mTiltXScale = 0; - mTiltYCenter = 0; - mTiltYScale = 0; - mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; - if (mHaveTilt) { - mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); - mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); - mTiltXScale = M_PI / 180; - mTiltYScale = M_PI / 180; - - mOrientedRanges.haveTilt = true; - - mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; - mOrientedRanges.tilt.source = mSource; - mOrientedRanges.tilt.min = 0; - mOrientedRanges.tilt.max = M_PI_2; - mOrientedRanges.tilt.flat = 0; - mOrientedRanges.tilt.fuzz = 0; - mOrientedRanges.tilt.resolution = 0; - } - - // Orientation - mOrientationScale = 0; - if (mHaveTilt) { - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI; - mOrientedRanges.orientation.max = M_PI; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } else if (mCalibration.orientationCalibration != - Calibration::ORIENTATION_CALIBRATION_NONE) { - if (mCalibration.orientationCalibration == - Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { - if (mRawPointerAxes.orientation.valid) { - if (mRawPointerAxes.orientation.maxValue > 0) { - mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; - } else if (mRawPointerAxes.orientation.minValue < 0) { - mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; - } else { - mOrientationScale = 0; - } - } - } - - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } - - // Distance - mDistanceScale = 0; - if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { - if (mCalibration.haveDistanceScale) { - mDistanceScale = mCalibration.distanceScale; - } else { - mDistanceScale = 1.0f; - } - } - - mOrientedRanges.haveDistance = true; - - mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; - mOrientedRanges.distance.source = mSource; - mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; - mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; - mOrientedRanges.distance.flat = 0; - mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; - mOrientedRanges.distance.resolution = 0; - } - - // Compute oriented precision, scales and ranges. - // Note that the maximum value reported is an inclusive maximum value so it is one - // unit less than the total width or height of surface. - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - case DISPLAY_ORIENTATION_270: - mOrientedXPrecision = mYPrecision; - mOrientedYPrecision = mXPrecision; - - mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - - mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; - break; - - default: - mOrientedXPrecision = mXPrecision; - mOrientedYPrecision = mYPrecision; - - mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - - mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; - break; - } - - // Location - updateAffineTransformation(); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Scale movements such that one whole swipe of the touch pad covers a - // given area relative to the diagonal size of the display when no acceleration - // is applied. - // Assume that the touch pad has a square aspect ratio such that movements in - // X and Y of the same number of raw units cover the same physical distance. - mPointerXMovementScale = - mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; - mPointerYMovementScale = mPointerXMovementScale; - - // Scale zooms to cover a smaller range of the display than movements do. - // This value determines the area around the pointer that is affected by freeform - // pointer gestures. - mPointerXZoomScale = - mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; - mPointerYZoomScale = mPointerXZoomScale; - - // Max width between pointers to detect a swipe gesture is more than some fraction - // of the diagonal axis of the touch pad. Touches that are wider than this are - // translated into freeform gestures. - mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - - // Abort current pointer usages because the state has changed. - abortPointerUsage(when, 0 /*policyFlags*/); - } - - // Inform the dispatcher about the changes. - *outResetNeeded = true; - bumpGeneration(); - } -} - -void TouchInputMapper::dumpSurface(std::string& dump) { - dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); - dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); - dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); - dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); - dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); -} - -void TouchInputMapper::configureVirtualKeys() { - std::vector virtualKeyDefinitions; - getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); - - mVirtualKeys.clear(); - - if (virtualKeyDefinitions.size() == 0) { - return; - } - - int32_t touchScreenLeft = mRawPointerAxes.x.minValue; - int32_t touchScreenTop = mRawPointerAxes.y.minValue; - int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); - int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); - - for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { - VirtualKey virtualKey; - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - int32_t dummyKeyMetaState; - uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, - &dummyKeyMetaState, &flags)) { - ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); - continue; // drop the key - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + - touchScreenLeft; - virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + - touchScreenLeft; - virtualKey.hitTop = - (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + - touchScreenTop; - virtualKey.hitBottom = - (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + - touchScreenTop; - mVirtualKeys.push_back(virtualKey); - } -} - -void TouchInputMapper::dumpVirtualKeys(std::string& dump) { - if (!mVirtualKeys.empty()) { - dump += INDENT3 "Virtual Keys:\n"; - - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; - dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, - virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const PropertyMap& in = getDevice()->getConfiguration(); - Calibration& out = mCalibration; - - // Size - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { - if (sizeCalibrationString == "none") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } else if (sizeCalibrationString == "geometric") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } else if (sizeCalibrationString == "diameter") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; - } else if (sizeCalibrationString == "box") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; - } else if (sizeCalibrationString == "area") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; - } else if (sizeCalibrationString != "default") { - ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); - } - } - - out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); - out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); - out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { - if (pressureCalibrationString == "none") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } else if (pressureCalibrationString == "physical") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } else if (pressureCalibrationString == "amplitude") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else if (pressureCalibrationString != "default") { - ALOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); - } - } - - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { - if (orientationCalibrationString == "none") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } else if (orientationCalibrationString == "interpolated") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else if (orientationCalibrationString == "vector") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; - } else if (orientationCalibrationString != "default") { - ALOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); - } - } - - // Distance - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; - String8 distanceCalibrationString; - if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { - if (distanceCalibrationString == "none") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } else if (distanceCalibrationString == "scaled") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } else if (distanceCalibrationString != "default") { - ALOGW("Invalid value for touch.distance.calibration: '%s'", - distanceCalibrationString.string()); - } - } - - out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); - - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; - String8 coverageCalibrationString; - if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { - if (coverageCalibrationString == "none") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } else if (coverageCalibrationString == "box") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; - } else if (coverageCalibrationString != "default") { - ALOGW("Invalid value for touch.coverage.calibration: '%s'", - coverageCalibrationString.string()); - } - } -} - -void TouchInputMapper::resolveCalibration() { - // Size - if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } - } else { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } - - // Pressure - if (mRawPointerAxes.pressure.valid) { - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } - } else { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } - - // Orientation - if (mRawPointerAxes.orientation.valid) { - if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } - } else { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } - - // Distance - if (mRawPointerAxes.distance.valid) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } - } else { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } - - // Coverage - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { - mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } -} - -void TouchInputMapper::dumpCalibration(std::string& dump) { - dump += INDENT3 "Calibration:\n"; - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump += INDENT4 "touch.size.calibration: none\n"; - break; - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - dump += INDENT4 "touch.size.calibration: geometric\n"; - break; - case Calibration::SIZE_CALIBRATION_DIAMETER: - dump += INDENT4 "touch.size.calibration: diameter\n"; - break; - case Calibration::SIZE_CALIBRATION_BOX: - dump += INDENT4 "touch.size.calibration: box\n"; - break; - case Calibration::SIZE_CALIBRATION_AREA: - dump += INDENT4 "touch.size.calibration: area\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveSizeScale) { - dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); - } - - if (mCalibration.haveSizeBias) { - dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); - } - - if (mCalibration.haveSizeIsSummed) { - dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", - toString(mCalibration.sizeIsSummed)); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump += INDENT4 "touch.pressure.calibration: none\n"; - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump += INDENT4 "touch.pressure.calibration: physical\n"; - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump += INDENT4 "touch.pressure.calibration: amplitude\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.havePressureScale) { - dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump += INDENT4 "touch.orientation.calibration: none\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump += INDENT4 "touch.orientation.calibration: interpolated\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: - dump += INDENT4 "touch.orientation.calibration: vector\n"; - break; - default: - ALOG_ASSERT(false); - } - - // Distance - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_NONE: - dump += INDENT4 "touch.distance.calibration: none\n"; - break; - case Calibration::DISTANCE_CALIBRATION_SCALED: - dump += INDENT4 "touch.distance.calibration: scaled\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveDistanceScale) { - dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); - } - - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_NONE: - dump += INDENT4 "touch.coverage.calibration: none\n"; - break; - case Calibration::COVERAGE_CALIBRATION_BOX: - dump += INDENT4 "touch.coverage.calibration: box\n"; - break; - default: - ALOG_ASSERT(false); - } -} - -void TouchInputMapper::dumpAffineTransformation(std::string& dump) { - dump += INDENT3 "Affine Transformation:\n"; - - dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); - dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); - dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); - dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); - dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); - dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); -} - -void TouchInputMapper::updateAffineTransformation() { - mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), - mSurfaceOrientation); -} - -void TouchInputMapper::reset(nsecs_t when) { - mCursorButtonAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - mTouchButtonAccumulator.reset(getDevice()); - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mRawStatesPending.clear(); - mCurrentRawState.clear(); - mCurrentCookedState.clear(); - mLastRawState.clear(); - mLastCookedState.clear(); - mPointerUsage = POINTER_USAGE_NONE; - mSentHoverEnter = false; - mHavePointerIds = false; - mCurrentMotionAborted = false; - mDownTime = 0; - - mCurrentVirtualKey.down = false; - - mPointerGesture.reset(); - mPointerSimple.reset(); - resetExternalStylus(); - - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } - - InputMapper::reset(when); -} - -void TouchInputMapper::resetExternalStylus() { - mExternalStylusState.clear(); - mExternalStylusId = -1; - mExternalStylusFusionTimeout = LLONG_MAX; - mExternalStylusDataPending = false; -} - -void TouchInputMapper::clearStylusDataPendingFlags() { - mExternalStylusDataPending = false; - mExternalStylusFusionTimeout = LLONG_MAX; -} - -void TouchInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void TouchInputMapper::sync(nsecs_t when) { - const RawState* last = - mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); - - // Push a new state. - mRawStatesPending.emplace_back(); - - RawState* next = &mRawStatesPending.back(); - next->clear(); - next->when = when; - - // Sync button state. - next->buttonState = - mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); - - // Sync scroll - next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); - next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); - mCursorScrollAccumulator.finishSync(); - - // Sync touch - syncTouch(when, next); - - // Assign pointer ids. - if (!mHavePointerIds) { - assignPointerIds(last, next); - } - -#if DEBUG_RAW_EVENTS - ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x", - last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, - last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, - last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); -#endif - - processRawTouches(false /*timeout*/); -} - -void TouchInputMapper::processRawTouches(bool timeout) { - if (mDeviceMode == DEVICE_MODE_DISABLED) { - // Drop all input if the device is disabled. - mCurrentRawState.clear(); - mRawStatesPending.clear(); - return; - } - - // Drain any pending touch states. The invariant here is that the mCurrentRawState is always - // valid and must go through the full cook and dispatch cycle. This ensures that anything - // touching the current state will only observe the events that have been dispatched to the - // rest of the pipeline. - const size_t N = mRawStatesPending.size(); - size_t count; - for (count = 0; count < N; count++) { - const RawState& next = mRawStatesPending[count]; - - // A failure to assign the stylus id means that we're waiting on stylus data - // and so should defer the rest of the pipeline. - if (assignExternalStylusId(next, timeout)) { - break; - } - - // All ready to go. - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); - if (mCurrentRawState.when < mLastRawState.when) { - mCurrentRawState.when = mLastRawState.when; - } - cookAndDispatch(mCurrentRawState.when); - } - if (count != 0) { - mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); - } - - if (mExternalStylusDataPending) { - if (timeout) { - nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, synthesizing event with new stylus data"); -#endif - cookAndDispatch(when); - } else if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::cookAndDispatch(nsecs_t when) { - // Always start with a clean state. - mCurrentCookedState.clear(); - - // Apply stylus buttons to current raw state. - applyExternalStylusButtonState(when); - - // Handle policy on initial down or hover events. - bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && - mCurrentRawState.rawPointerData.pointerCount != 0; - - uint32_t policyFlags = 0; - bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; - if (initialDown || buttonsPressed) { - // If this is a touch screen, hide the pointer on an initial down. - if (mDeviceMode == DEVICE_MODE_DIRECT) { - getContext()->fadePointer(); - } - - if (mParameters.wake) { - policyFlags |= POLICY_FLAG_WAKE; - } - } - - // Consume raw off-screen touches before cooking pointer data. - // If touches are consumed, subsequent code will not receive any pointer data. - if (consumeRawTouches(when, policyFlags)) { - mCurrentRawState.rawPointerData.clear(); - } - - // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure - // with cooked pointer data that has the same ids and indices as the raw data. - // The following code can use either the raw or cooked data, as needed. - cookPointerData(); - - // Apply stylus pressure to current cooked state. - applyExternalStylusTouchState(when); - - // Synthesize key down from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); - - // Dispatch the touches either directly or by translation through a pointer on screen. - if (mDeviceMode == DEVICE_MODE_POINTER) { - for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mCurrentCookedState.fingerIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { - mCurrentCookedState.mouseIdBits.markBit(id); - } - } - for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } - } - - // Stylus takes precedence over all tools, then mouse, then finger. - PointerUsage pointerUsage = mPointerUsage; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - mCurrentCookedState.mouseIdBits.clear(); - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_STYLUS; - } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_MOUSE; - } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || - isPointerDown(mCurrentRawState.buttonState)) { - pointerUsage = POINTER_USAGE_GESTURES; - } - - dispatchPointerUsage(when, policyFlags, pointerUsage); - } else { - if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && - mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, - mViewport.displayId); - } - - if (!mCurrentMotionAborted) { - dispatchButtonRelease(when, policyFlags); - dispatchHoverExit(when, policyFlags); - dispatchTouches(when, policyFlags); - dispatchHoverEnterAndMove(when, policyFlags); - dispatchButtonPress(when, policyFlags); - } - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentMotionAborted = false; - } - } - - // Synthesize key up from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, mLastCookedState.buttonState, - mCurrentCookedState.buttonState); - - // Clear some transient state. - mCurrentRawState.rawVScroll = 0; - mCurrentRawState.rawHScroll = 0; - - // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); -} - -void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; - } -} - -void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { - CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; - const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; - - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - - PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } - } -} - -bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { - if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { - return false; - } - - const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && - state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { -#if DEBUG_STYLUS_FUSION - ALOGD("Have both stylus and touch data, beginning fusion"); -#endif - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, assuming touch is not a stylus."); -#endif - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } -#if DEBUG_STYLUS_FUSION - ALOGD("No stylus data but stylus is connected, requesting timeout " - "(%" PRId64 "ms)", - mExternalStylusFusionTimeout); -#endif - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } - } - - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { -#if DEBUG_STYLUS_FUSION - ALOGD("Stylus pointer is going up"); -#endif - mExternalStylusId = -1; - } - - return false; -} - -void TouchInputMapper::timeoutExpired(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - if (mPointerUsage == POINTER_USAGE_GESTURES) { - dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); - } - } else if (mDeviceMode == DEVICE_MODE_DIRECT) { - if (mExternalStylusFusionTimeout < when) { - processRawTouches(true /*timeout*/); - } else if (mExternalStylusFusionTimeout != LLONG_MAX) { - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::updateExternalStylusState(const StylusState& state) { - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. - mExternalStylusDataPending = true; - processRawTouches(false /*timeout*/); - } -} - -bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { - // Check for release of a virtual key. - if (mCurrentVirtualKey.down) { - if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer went up while virtual key was down. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - return true; - } - - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation but do not consume the touch yet. - // This is useful when the user swipes through from the virtual key area - // into the main display surface. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, - mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | - AKEY_EVENT_FLAG_CANCELED); - } - } - - if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer just went down. Check for virtual key press or off-screen touches. - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (!isPointInsideSurface(pointer.x, pointer.y)) { - // If exactly one pointer went down, check for virtual key hit. - // Otherwise we will drop the entire stroke. - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey) { - mCurrentVirtualKey.down = true; - mCurrentVirtualKey.downTime = when; - mCurrentVirtualKey.keyCode = virtualKey->keyCode; - mCurrentVirtualKey.scanCode = virtualKey->scanCode; - mCurrentVirtualKey.ignored = - mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, - virtualKey->scanCode); - - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | - AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - } - } - return true; - } - } - - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch within the screen area. The idea is to filter out stray - // virtual key presses when interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mConfig.virtualKeyQuietTime > 0 && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); - } - return false; -} - -void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - int32_t keyCode = mCurrentVirtualKey.keyCode; - int32_t scanCode = mCurrentVirtualKey.scanCode; - nsecs_t downTime = mCurrentVirtualKey.downTime; - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, - scanCode, metaState, downTime); - getListener()->notifyKey(&args); -} - -void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - if (!currentIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mCurrentMotionAborted = true; - } -} - -void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - if (currentIdBits == lastIdBits) { - if (!currentIdBits.isEmpty()) { - // No pointer id changes so this is a move event. - // The listener takes care of batching moves so we don't have to deal with that here. - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } else { - // 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 moveIdBits(lastIdBits.value & currentIdBits.value); - BitSet32 dispatchedIdBits(lastIdBits.value); - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool moveNeeded = - updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, moveIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - - // Dispatch pointer up events. - while (!upIdBits.isEmpty()) { - uint32_t upId = upIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - dispatchedIdBits.clearBit(upId); - } - - // 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 && !moveIdBits.isEmpty()) { - ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - - // Dispatch pointer down events using the new pointer locations. - while (!downIdBits.isEmpty()) { - uint32_t downId = downIdBits.clearFirstMarkedBit(); - dispatchedIdBits.markBit(downId); - - if (dispatchedIdBits.count() == 1) { - // First pointer is going down. Set down time. - mDownTime = when; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, - downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } -} - -void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { - if (mSentHoverEnter && - (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || - !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { - int32_t metaState = getContext()->getGlobalMetaState(); - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, - mLastCookedState.buttonState, 0, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, - mOrientedYPrecision, mDownTime); - mSentHoverEnter = false; - } -} - -void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { - if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && - !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - if (!mSentHoverEnter) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = true; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { - BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!releasedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); - buttonState &= ~actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { - BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!pressedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); - buttonState |= actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { - if (!cookedPointerData.touchingIdBits.isEmpty()) { - return cookedPointerData.touchingIdBits; - } - return cookedPointerData.hoveringIdBits; -} - -void TouchInputMapper::cookPointerData() { - uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; - - mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; - mCurrentCookedState.cookedPointerData.hoveringIdBits = - mCurrentRawState.rawPointerData.hoveringIdBits; - mCurrentCookedState.cookedPointerData.touchingIdBits = - mCurrentRawState.rawPointerData.touchingIdBits; - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentCookedState.buttonState = 0; - } else { - mCurrentCookedState.buttonState = mCurrentRawState.buttonState; - } - - // Walk through the the active pointers and map device coordinates onto - // surface coordinates and adjust for display orientation. - for (uint32_t i = 0; i < currentPointerCount; i++) { - const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; - - // Size - float touchMajor, touchMinor, toolMajor, toolMinor, size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - case Calibration::SIZE_CALIBRATION_DIAMETER: - case Calibration::SIZE_CALIBRATION_BOX: - case Calibration::SIZE_CALIBRATION_AREA: - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { - touchMajor = in.touchMajor; - touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - toolMajor = in.toolMajor; - toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.touchMajor.valid) { - toolMajor = touchMajor = in.touchMajor; - toolMinor = touchMinor = - mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) - : in.touchMajor; - } else if (mRawPointerAxes.toolMajor.valid) { - touchMajor = toolMajor = in.toolMajor; - touchMinor = toolMinor = - mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; - } else { - ALOG_ASSERT(false, - "No touch or tool axes. " - "Size calibration should have been resolved to NONE."); - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - } - - if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { - uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); - if (touchingCount > 1) { - touchMajor /= touchingCount; - touchMinor /= touchingCount; - toolMajor /= touchingCount; - toolMinor /= touchingCount; - size /= touchingCount; - } - } - - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { - touchMajor *= mGeometricScale; - touchMinor *= mGeometricScale; - toolMajor *= mGeometricScale; - toolMinor *= mGeometricScale; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { - touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; - touchMinor = touchMajor; - toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; - toolMinor = toolMajor; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { - touchMinor = touchMajor; - toolMinor = toolMajor; - } - - mCalibration.applySizeScaleAndBias(&touchMajor); - mCalibration.applySizeScaleAndBias(&touchMinor); - mCalibration.applySizeScaleAndBias(&toolMajor); - mCalibration.applySizeScaleAndBias(&toolMinor); - size *= mSizeScale; - break; - default: - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - break; - } - - // Pressure - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = in.pressure * mPressureScale; - break; - default: - pressure = in.isHovering ? 0 : 1; - break; - } - - // Tilt and Orientation - float tilt; - float orientation; - if (mHaveTilt) { - float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; - float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); - tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); - } else { - tilt = 0; - - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mOrientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float confidence = hypotf(c1, c2); - float scale = 1.0f + confidence / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; - } else { - orientation = 0; - } - break; - } - default: - orientation = 0; - } - } - - // Distance - float distance; - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_SCALED: - distance = in.distance * mDistanceScale; - break; - default: - distance = 0; - } - - // Coverage - int32_t rawLeft, rawTop, rawRight, rawBottom; - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_BOX: - rawLeft = (in.toolMinor & 0xffff0000) >> 16; - rawRight = in.toolMinor & 0x0000ffff; - rawBottom = in.toolMajor & 0x0000ffff; - rawTop = (in.toolMajor & 0xffff0000) >> 16; - break; - default: - rawLeft = rawTop = rawRight = rawBottom = 0; - break; - } - - // Adjust X,Y coords for device calibration - // TODO: Adjust coverage coords? - float xTransformed = in.x, yTransformed = in.y; - mAffineTransform.applyTo(xTransformed, yTransformed); - - // Adjust X, Y, and coverage coords for surface orientation. - float x, y; - float left, top, right, bottom; - - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; - left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; - top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; - orientation -= M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { - orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; - y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; - left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; - right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; - bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; - top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; - orientation -= M_PI; - if (mOrientedRanges.haveOrientation && - orientation < mOrientedRanges.orientation.min) { - orientation += - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; - y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; - right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; - bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - orientation += M_PI_2; - if (mOrientedRanges.haveOrientation && - orientation > mOrientedRanges.orientation.max) { - orientation -= - (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - default: - x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - break; - } - - // Write output coords. - PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; - out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); - out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); - out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); - } else { - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); - } - - // Write output properties. - PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; - uint32_t id = in.id; - properties.clear(); - properties.id = id; - properties.toolType = in.toolType; - - // Write id index. - mCurrentCookedState.cookedPointerData.idToIndex[id] = i; - } -} - -void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, - PointerUsage pointerUsage) { - if (pointerUsage != mPointerUsage) { - abortPointerUsage(when, policyFlags); - mPointerUsage = pointerUsage; - } - - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); - break; - case POINTER_USAGE_STYLUS: - dispatchPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - dispatchPointerMouse(when, policyFlags); - break; - default: - break; - } -} - -void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - abortPointerGestures(when, policyFlags); - break; - case POINTER_USAGE_STYLUS: - abortPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - abortPointerMouse(when, policyFlags); - break; - default: - break; - } - - mPointerUsage = POINTER_USAGE_NONE; -} - -void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { - // Update current gesture coordinates. - bool cancelPreviousGesture, finishPreviousGesture; - bool sendEvents = - preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); - if (!sendEvents) { - return; - } - if (finishPreviousGesture) { - cancelPreviousGesture = false; - } - - // Update the pointer presentation and spots. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - if (finishPreviousGesture || cancelPreviousGesture) { - mPointerController->clearSpots(); - } - - if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, - mPointerController->getDisplayId()); - } - } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - } - - // Show or hide the pointer if needed. - switch (mPointerGesture.currentGestureMode) { - case PointerGesture::NEUTRAL: - case PointerGesture::QUIET: - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && - mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { - // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); - } - break; - case PointerGesture::TAP: - case PointerGesture::TAP_DRAG: - case PointerGesture::BUTTON_CLICK_OR_DRAG: - case PointerGesture::HOVER: - case PointerGesture::PRESS: - case PointerGesture::SWIPE: - // Unfade the pointer when the current gesture manipulates the - // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - break; - case PointerGesture::FREEFORM: - // Fade the pointer when the current gesture manipulates a different - // area and there are spots to guide the user experience. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - break; - } - - // Send events! - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || - mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || - mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || - mPointerGesture.currentGestureMode == PointerGesture::PRESS || - mPointerGesture.currentGestureMode == PointerGesture::SWIPE || - mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; - bool moveNeeded = false; - if (down && !cancelPreviousGesture && !finishPreviousGesture && - !mPointerGesture.lastGestureIdBits.isEmpty() && - !mPointerGesture.currentGestureIdBits.isEmpty()) { - BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & - mPointerGesture.lastGestureIdBits.value); - moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - } - - // Send motion events for all pointers that went up or were canceled. - BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); - if (!dispatchedGestureIdBits.isEmpty()) { - if (cancelPreviousGesture) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - - dispatchedGestureIdBits.clear(); - } else { - BitSet32 upGestureIdBits; - if (finishPreviousGesture) { - upGestureIdBits = dispatchedGestureIdBits; - } else { - upGestureIdBits.value = - dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; - } - while (!upGestureIdBits.isEmpty()) { - uint32_t id = upGestureIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); - - dispatchedGestureIdBits.clearBit(id); - } - } - } - - // Send motion events for all pointers that moved. - if (moveNeeded) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, - mPointerGesture.downTime); - } - - // Send motion events for all pointers that went down. - if (down) { - BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & - ~dispatchedGestureIdBits.value); - while (!downGestureIdBits.isEmpty()) { - uint32_t id = downGestureIdBits.clearFirstMarkedBit(); - dispatchedGestureIdBits.markBit(id); - - if (dispatchedGestureIdBits.count() == 1) { - mPointerGesture.downTime = when; - } - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, - 0, mPointerGesture.downTime); - } - } - - // Send motion events for hover. - if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); - } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { - // Synthesize a hover move event after all pointers go up to indicate that - // the pointer is hovering again even if the user is not currently touching - // the touch pad. This ensures that a view will receive a fresh hover enter - // event after a tap. - float x, y; - mPointerController->getPosition(&x, &y); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - PointerCoords pointerCoords; - pointerCoords.clear(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - - const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Update state. - mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; - if (!down) { - mPointerGesture.lastGestureIdBits.clear(); - } else { - mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; - for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - mPointerGesture.lastGestureProperties[index].copyFrom( - mPointerGesture.currentGestureProperties[index]); - mPointerGesture.lastGestureCoords[index].copyFrom( - mPointerGesture.currentGestureCoords[index]); - mPointerGesture.lastGestureIdToIndex[id] = index; - } - } -} - -void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { - // Cancel previously dispatches pointers. - if (!mPointerGesture.lastGestureIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, - mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } - - // Reset the current pointer gesture. - mPointerGesture.reset(); - mPointerVelocityControl.reset(); - - // Remove any current spots. - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } -} - -bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, - bool* outFinishPreviousGesture, bool isTimeout) { - *outCancelPreviousGesture = false; - *outFinishPreviousGesture = false; - - // Handle TAP timeout. - if (isTimeout) { -#if DEBUG_GESTURES - ALOGD("Gestures: Processing timeout"); -#endif - - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - // The tap/drag timeout has not yet expired. - getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + - mConfig.pointerGestureTapDragInterval); - } else { - // The tap is finished. -#if DEBUG_GESTURES - ALOGD("Gestures: TAP finished"); -#endif - *outFinishPreviousGesture = true; - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - return true; - } - } - - // We did not handle this timeout. - return false; - } - - const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); - const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); - - // Update the velocity tracker. - { - VelocityTracker::Position positions[MAX_POINTERS]; - uint32_t count = 0; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - positions[count].x = pointer.x * mPointerXMovementScale; - positions[count].y = pointer.y * mPointerYMovementScale; - } - mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - positions); - } - - // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning - // to NEUTRAL, then we should not generate tap event. - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && - mPointerGesture.lastGestureMode != PointerGesture::TAP && - mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { - mPointerGesture.resetTap(); - } - - // Pick a new active touch id if needed. - // Choose an arbitrary pointer that just went down, if there is one. - // Otherwise choose an arbitrary remaining pointer. - // This guarantees we always have an active touch id when there is at least one pointer. - // We keep the same active touch id for as long as possible. - int32_t lastActiveTouchId = mPointerGesture.activeTouchId; - int32_t activeTouchId = lastActiveTouchId; - if (activeTouchId < 0) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - mPointerGesture.firstTouchTime = when; - } - } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = false; - if (activeTouchId < 0) { - mPointerGesture.resetQuietTime(); - } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || - mPointerGesture.lastGestureMode == PointerGesture::SWIPE || - mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && - currentFingerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && - currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; - } - } - } - - // Switch states based on button and pointer state. - if (isQuietTime) { - // Case 1: Quiet time. (QUIET) -#if DEBUG_GESTURES - ALOGD("Gestures: QUIET for next %0.3fms", - (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { - *outFinishPreviousGesture = true; - } - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::QUIET; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - } else if (isPointerDown(mCurrentRawState.buttonState)) { - // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) - // The pointer follows the active touch point. - // Emit DOWN, MOVE, UP events at the pointer location. - // - // Only the active touch matters; other fingers are ignored. This policy helps - // to handle the case where the user places a second finger on the touch pad - // to apply the necessary force to depress an integrated button below the surface. - // We don't want the second finger to be delivered to applications. - // - // For this to work well, we need to make sure to track the pointer that is really - // active. If the user first puts one finger down to click then adds another - // finger to drag then the active pointer should switch to the finger that is - // being dragged. -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " - "currentFingerCount=%d", - activeTouchId, currentFingerCount); -#endif - // Reset state when just starting. - if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { - *outFinishPreviousGesture = true; - mPointerGesture.activeGestureId = 0; - } - - // Switch pointers if needed. - // Find the fastest pointer and follow it. - if (activeTouchId >= 0 && currentFingerCount > 1) { - int32_t bestId = -1; - float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); - if (speed > bestSpeed) { - bestId = id; - bestSpeed = speed; - } - } - } - if (bestId >= 0 && bestId != activeTouchId) { - mPointerGesture.activeTouchId = activeTouchId = bestId; -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " - "bestId=%d, bestSpeed=%0.3f", - bestId, bestSpeed); -#endif - } - } - - float deltaX = 0, deltaY = 0; - if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the click will occur at the position of the anchor - // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (currentFingerCount == 0) { - // Case 3. No fingers down and button is not pressed. (NEUTRAL) - if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { - *outFinishPreviousGesture = true; - } - - // Watch for taps coming out of HOVER or TAP_DRAG mode. - // Checking for taps after TAP_DRAG allows us to detect double-taps. - bool tapped = false; - if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || - mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && - lastFingerCount == 1) { - if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && - fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP"); -#endif - - mPointerGesture.tapUpTime = when; - getContext()->requestTimeoutAtTime(when + - mConfig.pointerGestureTapDragInterval); - - mPointerGesture.activeGestureId = 0; - mPointerGesture.currentGestureMode = PointerGesture::TAP; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = - mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.tapX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.tapY); - mPointerGesture.currentGestureCoords[0] - .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - tapped = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - if (mPointerGesture.tapDownTime != LLONG_MIN) { - ALOGD("Gestures: Not a TAP, %0.3fms since down", - (when - mPointerGesture.tapDownTime) * 0.000001f); - } else { - ALOGD("Gestures: Not a TAP, incompatible mode transitions"); - } -#endif - } - } - - mPointerVelocityControl.reset(); - - if (!tapped) { -#if DEBUG_GESTURES - ALOGD("Gestures: NEUTRAL"); -#endif - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - } - } else if (currentFingerCount == 1) { - // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) - // The pointer follows the active touch point. - // When in HOVER, emit HOVER_MOVE events at the pointer location. - // When in TAP_DRAG, emit MOVE events at the pointer location. - ALOG_ASSERT(activeTouchId >= 0); - - mPointerGesture.currentGestureMode = PointerGesture::HOVER; - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && - fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", - (when - mPointerGesture.tapUpTime) * 0.000001f); -#endif - } - } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } - - float deltaX = 0, deltaY = 0; - if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - bool down; - if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP_DRAG"); -#endif - down = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: HOVER"); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { - *outFinishPreviousGesture = true; - } - mPointerGesture.activeGestureId = 0; - down = false; - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - down ? 1.0f : 0.0f); - - if (lastFingerCount == 0 && currentFingerCount != 0) { - mPointerGesture.resetTap(); - mPointerGesture.tapDownTime = when; - mPointerGesture.tapX = x; - mPointerGesture.tapY = y; - } - } else { - // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) - // We need to provide feedback for each finger that goes down so we cannot wait - // for the fingers to move before deciding what to do. - // - // The ambiguous case is deciding what to do when there are two fingers down but they - // have not moved enough to determine whether they are part of a drag or part of a - // freeform gesture, or just a press or long-press at the pointer location. - // - // When there are two fingers we start with the PRESS hypothesis and we generate a - // down at the pointer location. - // - // When the two fingers move enough or when additional fingers are added, we make - // a decision to transition into SWIPE or FREEFORM mode accordingly. - ALOG_ASSERT(activeTouchId >= 0); - - bool settled = when >= - mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; - if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && - mPointerGesture.lastGestureMode != PointerGesture::SWIPE && - mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - *outFinishPreviousGesture = true; - } else if (!settled && currentFingerCount > lastFingerCount) { - // Additional pointers have gone down but not yet settled. - // Reset the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - - when) * 0.000001f); -#endif - *outCancelPreviousGesture = true; - } else { - // Continue previous gesture. - mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; - } - - if (*outFinishPreviousGesture || *outCancelPreviousGesture) { - mPointerGesture.currentGestureMode = PointerGesture::PRESS; - mPointerGesture.activeGestureId = 0; - mPointerGesture.referenceIdBits.clear(); - mPointerVelocityControl.reset(); - - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - - when) * 0.000001f); -#endif - mCurrentRawState.rawPointerData - .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } - - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & - ~mPointerGesture.referenceIdBits.value); - !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; - - // Add delta for all fingers and calculate a common movement delta. - float commonDeltaX = 0, commonDeltaY = 0; - BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value); - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { - bool first = (idBits == commonIdBits); - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); - const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } - } - - // Consider transitions from PRESS to SWIPE or MULTITOUCH. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float dist[MAX_POINTER_ID + 1]; - int32_t distOverThreshold = 0; - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); - if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { - distOverThreshold += 1; - } - } - - // Only transition when at least two pointers have moved further than - // the minimum distance threshold. - if (distOverThreshold >= 2) { - if (currentFingerCount > 2) { - // There are more than two pointers, switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are exactly two pointers. - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - uint32_t id1 = idBits.clearFirstMarkedBit(); - uint32_t id2 = idBits.firstMarkedBit(); - const RawPointerData::Pointer& p1 = - mCurrentRawState.rawPointerData.pointerForId(id1); - const RawPointerData::Pointer& p2 = - mCurrentRawState.rawPointerData.pointerForId(id2); - float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); - if (mutualDistance > mPointerGestureMaxSwipeWidth) { - // There are two pointers but they are too far apart for a SWIPE, - // switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - mutualDistance, mPointerGestureMaxSwipeWidth); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - float dist1 = dist[id1]; - float dist2 = dist[id2]; - if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && - dist2 >= mConfig.pointerGestureMultitouchMinDistance) { - // Calculate the dot product of the displacement vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * - // mag(v2). - PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; - PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; - float dx1 = delta1.dx * mPointerXZoomScale; - float dy1 = delta1.dy * mPointerYZoomScale; - float dx2 = delta2.dx * mPointerXZoomScale; - float dy2 = delta2.dy * mPointerYZoomScale; - float dot = dx1 * dx2 + dy1 * dy2; - float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to SWIPE, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, - mConfig.pointerGestureMultitouchMinDistance, cosine, - mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - } - } - } - } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // Switch from SWIPE to FREEFORM if additional pointers go down. - // Cancel previous gesture. - if (currentFingerCount > 2) { -#if DEBUG_GESTURES - ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - - // Move the reference points based on the overall group motion of the fingers - // except in PRESS mode while waiting for a transition to occur. - if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && - (commonDeltaX || commonDeltaY)) { - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; - - commonDeltaX *= mPointerXMovementScale; - commonDeltaY *= mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); - mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - } - - // Report gestures. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || - mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // PRESS or SWIPE mode. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - // FREEFORM mode. -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - - BitSet32 mappedTouchIdBits; - BitSet32 usedGestureIdBits; - if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - // Initially, assign the active gesture id to the active touch point - // if there is one. No other touch id bits are mapped yet. - if (!*outCancelPreviousGesture) { - mappedTouchIdBits.markBit(activeTouchId); - usedGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = - mPointerGesture.activeGestureId; - } else { - mPointerGesture.activeGestureId = -1; - } - } else { - // Otherwise, assume we mapped all touches from the previous frame. - // Reuse all mappings that are still applicable. - mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & - mCurrentCookedState.fingerIdBits.value; - usedGestureIdBits = mPointerGesture.lastGestureIdBits; - - // Check whether we need to choose a new active gesture id because the - // current went went up. - for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & - ~mCurrentCookedState.fingerIdBits.value); - !upTouchIdBits.isEmpty();) { - uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); - uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; - if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { - mPointerGesture.activeGestureId = -1; - break; - } - } - } - -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM follow up " - "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " - "activeGestureId=%d", - mappedTouchIdBits.value, usedGestureIdBits.value, - mPointerGesture.activeGestureId); -#endif - - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - for (uint32_t i = 0; i < currentFingerCount; i++) { - uint32_t touchId = idBits.clearFirstMarkedBit(); - uint32_t gestureId; - if (!mappedTouchIdBits.hasBit(touchId)) { - gestureId = usedGestureIdBits.markFirstUnmarkedBit(); - mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "new mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } else { - gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "existing mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } - mPointerGesture.currentGestureIdBits.markBit(gestureId); - mPointerGesture.currentGestureIdToIndex[gestureId] = i; - - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(touchId); - float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; - float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - - mPointerGesture.currentGestureProperties[i].clear(); - mPointerGesture.currentGestureProperties[i].id = gestureId; - mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[i].clear(); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX + deltaX); - mPointerGesture.currentGestureCoords[i] - .setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY + deltaY); - mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - 1.0f); - } - - if (mPointerGesture.activeGestureId < 0) { - mPointerGesture.activeGestureId = - mPointerGesture.currentGestureIdBits.firstMarkedBit(); -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM new " - "activeGestureId=%d", - mPointerGesture.activeGestureId); -#endif - } - } - } - - mPointerController->setButtonState(mCurrentRawState.buttonState); - -#if DEBUG_GESTURES - ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGestureIdBits=0x%08x", - toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); - for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; - ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } - for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; - ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } -#endif - return true; -} - -void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); - uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); - float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); - mPointerController->setPosition(x, y); - - hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); - down = !hovering; - - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[index]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; - } else { - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); -} - -void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); - uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - float deltaX = 0, deltaY = 0; - if (mLastCookedState.mouseIdBits.hasBit(id)) { - uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - - mLastRawState.rawPointerData.pointers[lastIndex].x) * - mPointerXMovementScale; - deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - - mLastRawState.rawPointerData.pointers[lastIndex].y) * - mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - down = isPointerDown(mCurrentRawState.buttonState); - hovering = !down; - - float x, y; - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - hovering ? 0.0f : 1.0f); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; - } else { - mPointerVelocityControl.reset(); - - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); - - mPointerVelocityControl.reset(); -} - -void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, - bool hovering) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t displayId = mViewport.displayId; - - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); - - float xCursorPosition; - float yCursorPosition; - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - - if (mPointerSimple.down && !down) { - mPointerSimple.down = false; - - // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mPointerSimple.hovering && !hovering) { - mPointerSimple.hovering = false; - - // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (down) { - if (!mPointerSimple.down) { - mPointerSimple.down = true; - mPointerSimple.downTime = when; - - // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (hovering) { - if (!mPointerSimple.hovering) { - mPointerSimple.hovering = true; - - // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { - float vscroll = mCurrentRawState.rawVScroll; - float hscroll = mCurrentRawState.rawHScroll; - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - // Send scroll. - PointerCoords pointerCoords; - pointerCoords.copyFrom(mPointerSimple.currentCoords); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Save state. - if (down || hovering) { - mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); - mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); - } else { - mPointerSimple.reset(); - } -} - -void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - dispatchPointerSimple(when, policyFlags, false, false); -} - -void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, int32_t edgeFlags, - const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, - BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime) { - PointerCoords pointerCoords[MAX_POINTERS]; - PointerProperties pointerProperties[MAX_POINTERS]; - uint32_t pointerCount = 0; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = idToIndex[id]; - pointerProperties[pointerCount].copyFrom(properties[index]); - pointerCoords[pointerCount].copyFrom(coords[index]); - - if (changedId >= 0 && id == uint32_t(changedId)) { - action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - - pointerCount += 1; - } - - ALOG_ASSERT(pointerCount != 0); - - if (changedId >= 0 && pointerCount == 1) { - // Replace initial down and final up action. - // We can compare the action without masking off the changed pointer index - // because we know the index is 0. - if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { - action = AMOTION_EVENT_ACTION_DOWN; - } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { - action = AMOTION_EVENT_ACTION_UP; - } else { - // Can't happen. - ALOG_ASSERT(false); - } - } - float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; - if (mDeviceMode == DEVICE_MODE_POINTER) { - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - } - const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); - const int32_t deviceId = getDeviceId(); - std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); - std::for_each(frames.begin(), frames.end(), - [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, - pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, - downTime, std::move(frames)); - getListener()->notifyMotion(&args); -} - -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, - const uint32_t* inIdToIndex, - PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; -} - -void TouchInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -void TouchInputMapper::cancelTouch(nsecs_t when) { - abortPointerUsage(when, 0 /*policyFlags*/); - abortTouches(when, 0 /* policyFlags*/); -} - -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - const float scaledX = x * mXScale; - const float scaledY = y * mYScale; - return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && - y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; -} - -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { - for (const VirtualKey& virtualKey : mVirtualKeys) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return &virtualKey; - } - } - - return nullptr; -} - -void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { - uint32_t currentPointerCount = current->rawPointerData.pointerCount; - uint32_t lastPointerCount = last->rawPointerData.pointerCount; - - current->rawPointerData.clearIdBits(); - - if (currentPointerCount == 0) { - // No pointers to assign. - return; - } - - if (lastPointerCount == 0) { - // All pointers are new. - for (uint32_t i = 0; i < currentPointerCount; i++) { - uint32_t id = i; - current->rawPointerData.pointers[i].id = id; - current->rawPointerData.idToIndex[id] = i; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); - } - return; - } - - if (currentPointerCount == 1 && lastPointerCount == 1 && - current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = last->rawPointerData.pointers[0].id; - current->rawPointerData.pointers[0].id = id; - current->rawPointerData.idToIndex[id] = 0; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); - return; - } - - // General case. - // 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. - // The pointers must have the same tool type but it is possible for them to - // transition from hovering to touching or vice-versa while retaining the same id. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - const RawPointerData::Pointer& currentPointer = - current->rawPointerData.pointers[currentPointerIndex]; - const RawPointerData::Pointer& lastPointer = - last->rawPointerData.pointers[lastPointerIndex]; - if (currentPointer.toolType == lastPointer.toolType) { - int64_t deltaX = currentPointer.x - lastPointer.x; - int64_t deltaY = currentPointer.y - lastPointer.y; - - 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; - } - } - } - - // 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; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, 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. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { - while (heapSize > 0) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0;;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize && - heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, - heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); - } -#endif - } - - heapSize -= 1; - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering( - currentPointerIndex)); - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 - ", distance=%" PRIu64, - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to pointers that were not matched in the process. - for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { - uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); - uint32_t id = usedIdBits.markFirstUnmarkedBit(); - - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); -#endif - } -} - -int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.keyCode == keyCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.scanCode == scanCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - for (const VirtualKey& virtualKey : mVirtualKeys) { - for (size_t i = 0; i < numCodes; i++) { - if (virtualKey.keyCode == keyCodes[i]) { - outFlags[i] = 1; - } - } - } - - return true; -} - -std::optional TouchInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - return std::make_optional(mViewport.displayId); - } - } - return std::nullopt; -} - -} // namespace android diff --git a/services/inputflinger/reader/VibratorInputMapper.cpp b/services/inputflinger/reader/VibratorInputMapper.cpp deleted file mode 100644 index a27fab4581..0000000000 --- a/services/inputflinger/reader/VibratorInputMapper.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Macros.h" - -#include "VibratorInputMapper.h" - -namespace android { - -VibratorInputMapper::VibratorInputMapper(InputDevice* device) - : InputMapper(device), mVibrating(false) {} - -VibratorInputMapper::~VibratorInputMapper() {} - -uint32_t VibratorInputMapper::getSources() { - return 0; -} - -void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setVibrator(true); -} - -void VibratorInputMapper::process(const RawEvent* rawEvent) { - // TODO: Handle FF_STATUS, although it does not seem to be widely supported. -} - -void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -#if DEBUG_VIBRATOR - std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } - ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), - patternStr.c_str(), repeat, token); -#endif - - mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; - mRepeat = repeat; - mToken = token; - mIndex = -1; - - nextStep(); -} - -void VibratorInputMapper::cancelVibrate(int32_t token) { -#if DEBUG_VIBRATOR - ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); -#endif - - if (mVibrating && mToken == token) { - stopVibrating(); - } -} - -void VibratorInputMapper::timeoutExpired(nsecs_t when) { - if (mVibrating) { - if (when >= mNextStepTime) { - nextStep(); - } else { - getContext()->requestTimeoutAtTime(mNextStepTime); - } - } -} - -void VibratorInputMapper::nextStep() { - mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { - if (mRepeat < 0) { - // We are done. - stopVibrating(); - return; - } - mIndex = mRepeat; - } - - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); -#endif - getEventHub()->vibrate(getDeviceId(), duration); - } else { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); - } - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; - getContext()->requestTimeoutAtTime(mNextStepTime); -#if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); -#endif -} - -void VibratorInputMapper::stopVibrating() { - mVibrating = false; -#if DEBUG_VIBRATOR - ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); -} - -void VibratorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Vibrator Input Mapper:\n"; - dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); -} - -} // namespace android diff --git a/services/inputflinger/reader/VibratorInputMapper.h b/services/inputflinger/reader/VibratorInputMapper.h deleted file mode 100644 index 6b33f4811e..0000000000 --- a/services/inputflinger/reader/VibratorInputMapper.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H -#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H - -#include "InputMapper.h" - -namespace android { - -class VibratorInputMapper : public InputMapper { -public: - explicit VibratorInputMapper(InputDevice* device); - virtual ~VibratorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); - -private: - bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; - ssize_t mRepeat; - int32_t mToken; - ssize_t mIndex; - nsecs_t mNextStepTime; - - void nextStep(); - void stopVibrating(); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorButtonAccumulator.h b/services/inputflinger/reader/include/CursorButtonAccumulator.h deleted file mode 100644 index d9123109a3..0000000000 --- a/services/inputflinger/reader/include/CursorButtonAccumulator.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H -#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H - -#include - -namespace android { - -class InputDevice; -struct RawEvent; - -/* Keeps track of the state of mouse or touch pad buttons. */ -class CursorButtonAccumulator { -public: - CursorButtonAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - -private: - bool mBtnLeft; - bool mBtnRight; - bool mBtnMiddle; - bool mBtnBack; - bool mBtnSide; - bool mBtnForward; - bool mBtnExtra; - bool mBtnTask; - - void clearButtons(); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorInputMapper.h b/services/inputflinger/reader/include/CursorInputMapper.h deleted file mode 100644 index 52e42a7991..0000000000 --- a/services/inputflinger/reader/include/CursorInputMapper.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H -#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H - -#include "CursorButtonAccumulator.h" -#include "CursorScrollAccumulator.h" -#include "InputMapper.h" - -#include -#include - -namespace android { - -class VelocityControl; -class PointerControllerInterface; - -class CursorButtonAccumulator; -class CursorScrollAccumulator; - -/* Keeps track of cursor movements. */ -class CursorMotionAccumulator { -public: - CursorMotionAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - -private: - int32_t mRelX; - int32_t mRelY; - - void clearRelativeAxes(); -}; - -class CursorInputMapper : public InputMapper { -public: - explicit CursorInputMapper(InputDevice* device); - virtual ~CursorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - - virtual void fadePointer(); - - virtual std::optional getAssociatedDisplayId(); - -private: - // Amount that trackball needs to move in order to generate a key event. - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; - - // Immutable configuration parameters. - struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, - }; - - Mode mode; - bool hasAssociatedDisplay; - bool orientationAware; - } mParameters; - - CursorButtonAccumulator mCursorButtonAccumulator; - CursorMotionAccumulator mCursorMotionAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - - int32_t mSource; - float mXScale; - float mYScale; - float mXPrecision; - float mYPrecision; - - float mVWheelScale; - float mHWheelScale; - - // Velocity controls for mouse pointer and wheel movements. - // The controls for X and Y wheel movements are separate to keep them decoupled. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - int32_t mOrientation; - - sp mPointerController; - - int32_t mButtonState; - nsecs_t mDownTime; - - void configureParameters(); - void dumpParameters(std::string& dump); - - void sync(nsecs_t when); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/CursorScrollAccumulator.h b/services/inputflinger/reader/include/CursorScrollAccumulator.h deleted file mode 100644 index 85f331fd8a..0000000000 --- a/services/inputflinger/reader/include/CursorScrollAccumulator.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H -#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H - -#include - -namespace android { - -class InputDevice; -struct RawEvent; - -/* Keeps track of cursor scrolling motions. */ - -class CursorScrollAccumulator { -public: - CursorScrollAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline bool haveRelativeVWheel() const { return mHaveRelWheel; } - inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - inline int32_t getRelativeVWheel() const { return mRelWheel; } - inline int32_t getRelativeHWheel() const { return mRelHWheel; } - -private: - bool mHaveRelWheel; - bool mHaveRelHWheel; - - int32_t mRelX; - int32_t mRelY; - int32_t mRelWheel; - int32_t mRelHWheel; - - void clearRelativeAxes(); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/InputMapper.h b/services/inputflinger/reader/include/InputMapper.h deleted file mode 100644 index a559ef882f..0000000000 --- a/services/inputflinger/reader/include/InputMapper.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_INPUT_MAPPER_H -#define _UI_INPUTREADER_INPUT_MAPPER_H - -#include "EventHub.h" -#include "InputDevice.h" -#include "InputListener.h" -#include "InputReaderContext.h" -#include "StylusState.h" - -namespace android { - -/* An input mapper transforms raw input events into cooked event data. - * A single input device can have multiple associated input mappers in order to interpret - * different classes of events. - * - * InputMapper lifecycle: - * - create - * - configure with 0 changes - * - reset - * - process, process, process (may occasionally reconfigure with non-zero changes or reset) - * - reset - * - destroy - */ -class InputMapper { -public: - explicit InputMapper(InputDevice* device); - virtual ~InputMapper(); - - inline InputDevice* getDevice() { return mDevice; } - inline int32_t getDeviceId() { return mDevice->getId(); } - inline const std::string getDeviceName() { return mDevice->getName(); } - inline InputReaderContext* getContext() { return mContext; } - inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline InputListenerInterface* getListener() { return mContext->getListener(); } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - virtual uint32_t getSources() = 0; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent) = 0; - virtual void timeoutExpired(nsecs_t when); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void cancelTouch(nsecs_t when); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - - virtual void updateExternalStylusState(const StylusState& state); - - virtual void fadePointer(); - virtual std::optional getAssociatedDisplayId() { return std::nullopt; } - -protected: - InputDevice* mDevice; - InputReaderContext* mContext; - - status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); - void bumpGeneration(); - - static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, - const char* name); - static void dumpStylusState(std::string& dump, const StylusState& state); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/include/KeyboardInputMapper.h b/services/inputflinger/reader/include/KeyboardInputMapper.h deleted file mode 100644 index 2665c0258c..0000000000 --- a/services/inputflinger/reader/include/KeyboardInputMapper.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H -#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H - -#include "InputMapper.h" - -namespace android { - -class KeyboardInputMapper : public InputMapper { -public: - KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - virtual std::optional getAssociatedDisplayId(); - -private: - // The current viewport. - std::optional mViewport; - - struct KeyDown { - int32_t keyCode; - int32_t scanCode; - }; - - uint32_t mSource; - int32_t mKeyboardType; - - std::vector mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down - - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none - - struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on - }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; - - // Immutable configuration parameters. - struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - } mParameters; - - void configureParameters(); - void dumpParameters(std::string& dump); - - int32_t getOrientation(); - int32_t getDisplayId(); - - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - - void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); - - bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - - ssize_t findKeyDown(int32_t scanCode); - - void resetLedState(); - void initializeLedState(LedState& ledState, int32_t led); - void updateLedState(bool reset); - void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); - std::optional findViewport(nsecs_t when, - const InputReaderConfiguration* config); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/MultiTouchInputMapper.h b/services/inputflinger/reader/include/MultiTouchInputMapper.h deleted file mode 100644 index 87841ebeec..0000000000 --- a/services/inputflinger/reader/include/MultiTouchInputMapper.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H - -#include "TouchInputMapper.h" - -namespace android { - -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; - } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; - } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse; - bool mHaveAbsMTTouchMinor; - bool mHaveAbsMTWidthMinor; - bool mHaveAbsMTToolType; - - int32_t mAbsMTPositionX; - int32_t mAbsMTPositionY; - int32_t mAbsMTTouchMajor; - int32_t mAbsMTTouchMinor; - int32_t mAbsMTWidthMajor; - int32_t mAbsMTWidthMinor; - int32_t mAbsMTOrientation; - int32_t mAbsMTTrackingId; - int32_t mAbsMTPressure; - int32_t mAbsMTDistance; - int32_t mAbsMTToolType; - - Slot(); - void clear(); - }; - - MultiTouchMotionAccumulator(); - ~MultiTouchMotionAccumulator(); - - void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); - void reset(InputDevice* device); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlotCount; } - inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - -private: - int32_t mCurrentSlot; - Slot* mSlots; - size_t mSlotCount; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void clearSlots(int32_t initialSlot); -}; - -class MultiTouchInputMapper : public TouchInputMapper { -public: - explicit MultiTouchInputMapper(InputDevice* device); - virtual ~MultiTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; - - // Specifies the pointer id bits that are in use, and their associated tracking id. - BitSet32 mPointerIdBits; - int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; -}; - -} // namespace android - -#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/SingleTouchInputMapper.h b/services/inputflinger/reader/include/SingleTouchInputMapper.h deleted file mode 100644 index d6b1455b68..0000000000 --- a/services/inputflinger/reader/include/SingleTouchInputMapper.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H - -#include "SingleTouchMotionAccumulator.h" -#include "TouchInputMapper.h" - -namespace android { - -class SingleTouchInputMapper : public TouchInputMapper { -public: - explicit SingleTouchInputMapper(InputDevice* device); - virtual ~SingleTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; -}; - -} // namespace android - -#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h deleted file mode 100644 index 75f8a961b3..0000000000 --- a/services/inputflinger/reader/include/SingleTouchMotionAccumulator.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H -#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H - -#include - -namespace android { - -class InputDevice; -struct RawEvent; - -/* Keeps track of the state of single-touch protocol. */ -class SingleTouchMotionAccumulator { -public: - SingleTouchMotionAccumulator(); - - void process(const RawEvent* rawEvent); - void reset(InputDevice* device); - - inline int32_t getAbsoluteX() const { return mAbsX; } - inline int32_t getAbsoluteY() const { return mAbsY; } - inline int32_t getAbsolutePressure() const { return mAbsPressure; } - inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } - inline int32_t getAbsoluteDistance() const { return mAbsDistance; } - inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } - inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } - -private: - int32_t mAbsX; - int32_t mAbsY; - int32_t mAbsPressure; - int32_t mAbsToolWidth; - int32_t mAbsDistance; - int32_t mAbsTiltX; - int32_t mAbsTiltY; - - void clearAbsoluteAxes(); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/SwitchInputMapper.h b/services/inputflinger/reader/include/SwitchInputMapper.h deleted file mode 100644 index dd4bb9ed65..0000000000 --- a/services/inputflinger/reader/include/SwitchInputMapper.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H - -#include "InputMapper.h" - -namespace android { - -class SwitchInputMapper : public InputMapper { -public: - explicit SwitchInputMapper(InputDevice* device); - virtual ~SwitchInputMapper(); - - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); - -private: - uint32_t mSwitchValues; - uint32_t mUpdatedSwitchMask; - - void processSwitch(int32_t switchCode, int32_t switchValue); - void sync(nsecs_t when); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/TouchButtonAccumulator.h b/services/inputflinger/reader/include/TouchButtonAccumulator.h deleted file mode 100644 index 65b6bdcc12..0000000000 --- a/services/inputflinger/reader/include/TouchButtonAccumulator.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H -#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H - -#include - -namespace android { - -class InputDevice; -struct RawEvent; - -/* Keeps track of the state of touch, stylus and tool buttons. */ -class TouchButtonAccumulator { -public: - TouchButtonAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - int32_t getToolType() const; - bool isToolActive() const; - bool isHovering() const; - bool hasStylus() const; - -private: - bool mHaveBtnTouch; - bool mHaveStylus; - - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; - - void clearButtons(); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/include/TouchInputMapper.h b/services/inputflinger/reader/include/TouchInputMapper.h deleted file mode 100644 index cf6cd76b87..0000000000 --- a/services/inputflinger/reader/include/TouchInputMapper.h +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H -#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H - -#include "CursorButtonAccumulator.h" -#include "CursorScrollAccumulator.h" -#include "EventHub.h" -#include "InputMapper.h" -#include "InputReaderBase.h" -#include "TouchButtonAccumulator.h" - -#include - -namespace android { - -/* Raw axis information from the driver. */ -struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); - inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } - inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); -}; - -/* Raw data for a collection of pointers including a pointer id mapping table. */ -struct RawPointerData { - struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; - }; - - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); - void getCentroidOfTouchingPointers(float* outX, float* outY) const; - - inline void markIdBit(uint32_t id, bool isHovering) { - if (isHovering) { - hoveringIdBits.markBit(id); - } else { - touchingIdBits.markBit(id); - } - } - - inline void clearIdBits() { - hoveringIdBits.clear(); - touchingIdBits.clear(); - } - - inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } - - inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } -}; - -/* Cooked data for a collection of pointers including a pointer id mapping table. */ -struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); - - inline const PointerCoords& pointerCoordsForId(uint32_t id) const { - return pointerCoords[idToIndex[id]]; - } - - inline PointerCoords& editPointerCoordsWithId(uint32_t id) { - return pointerCoords[idToIndex[id]]; - } - - inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { - return pointerProperties[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) const { - return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); - } - - inline bool isTouching(uint32_t pointerIndex) const { - return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); - } -}; - -class TouchInputMapper : public InputMapper { -public: - explicit TouchInputMapper(InputDevice* device); - virtual ~TouchInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional getAssociatedDisplayId(); - -protected: - CursorButtonAccumulator mCursorButtonAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - TouchButtonAccumulator mTouchButtonAccumulator; - - struct VirtualKey { - int32_t keyCode; - int32_t scanCode; - uint32_t flags; - - // computed hit box, specified in touch screen coords based on known display size - int32_t hitLeft; - int32_t hitTop; - int32_t hitRight; - int32_t hitBottom; - - inline bool isHit(int32_t x, int32_t y) const { - return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; - } - }; - - // Input sources and device mode. - uint32_t mSource; - - enum DeviceMode { - DEVICE_MODE_DISABLED, // input is disabled - DEVICE_MODE_DIRECT, // direct mapping (touchscreen) - DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) - DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - DEVICE_MODE_POINTER, // pointer mapping (pointer) - }; - DeviceMode mDeviceMode; - - // The reader's configuration. - InputReaderConfiguration mConfig; - - // Immutable configuration parameters. - struct Parameters { - enum DeviceType { - DEVICE_TYPE_TOUCH_SCREEN, - DEVICE_TYPE_TOUCH_PAD, - DEVICE_TYPE_TOUCH_NAVIGATION, - DEVICE_TYPE_POINTER, - }; - - DeviceType deviceType; - bool hasAssociatedDisplay; - bool associatedDisplayIsExternal; - bool orientationAware; - bool hasButtonUnderPad; - std::string uniqueDisplayId; - - enum GestureMode { - GESTURE_MODE_SINGLE_TOUCH, - GESTURE_MODE_MULTI_TOUCH, - }; - GestureMode gestureMode; - - bool wake; - } mParameters; - - // Immutable calibration parameters in parsed form. - struct Calibration { - // Size - enum SizeCalibration { - SIZE_CALIBRATION_DEFAULT, - SIZE_CALIBRATION_NONE, - SIZE_CALIBRATION_GEOMETRIC, - SIZE_CALIBRATION_DIAMETER, - SIZE_CALIBRATION_BOX, - SIZE_CALIBRATION_AREA, - }; - - SizeCalibration sizeCalibration; - - bool haveSizeScale; - float sizeScale; - bool haveSizeBias; - float sizeBias; - bool haveSizeIsSummed; - bool sizeIsSummed; - - // Pressure - enum PressureCalibration { - PRESSURE_CALIBRATION_DEFAULT, - PRESSURE_CALIBRATION_NONE, - PRESSURE_CALIBRATION_PHYSICAL, - PRESSURE_CALIBRATION_AMPLITUDE, - }; - - PressureCalibration pressureCalibration; - bool havePressureScale; - float pressureScale; - - // Orientation - enum OrientationCalibration { - ORIENTATION_CALIBRATION_DEFAULT, - ORIENTATION_CALIBRATION_NONE, - ORIENTATION_CALIBRATION_INTERPOLATED, - ORIENTATION_CALIBRATION_VECTOR, - }; - - OrientationCalibration orientationCalibration; - - // Distance - enum DistanceCalibration { - DISTANCE_CALIBRATION_DEFAULT, - DISTANCE_CALIBRATION_NONE, - DISTANCE_CALIBRATION_SCALED, - }; - - DistanceCalibration distanceCalibration; - bool haveDistanceScale; - float distanceScale; - - enum CoverageCalibration { - COVERAGE_CALIBRATION_DEFAULT, - COVERAGE_CALIBRATION_NONE, - COVERAGE_CALIBRATION_BOX, - }; - - CoverageCalibration coverageCalibration; - - inline void applySizeScaleAndBias(float* outSize) const { - if (haveSizeScale) { - *outSize *= sizeScale; - } - if (haveSizeBias) { - *outSize += sizeBias; - } - if (*outSize < 0) { - *outSize = 0; - } - } - } mCalibration; - - // Affine location transformation/calibration - struct TouchAffineTransformation mAffineTransform; - - RawPointerAxes mRawPointerAxes; - - struct RawState { - nsecs_t when; - - // Raw pointer sample data. - RawPointerData rawPointerData; - - int32_t buttonState; - - // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - void copyFrom(const RawState& other) { - when = other.when; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } - - void clear() { - when = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } - }; - - struct CookedState { - // Cooked pointer sample data. - CookedPointerData cookedPointerData; - - // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } - - void clear() { - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } - }; - - std::vector mRawStatesPending; - RawState mCurrentRawState; - CookedState mCurrentCookedState; - RawState mLastRawState; - CookedState mLastCookedState; - - // State provided by an external stylus - StylusState mExternalStylusState; - int64_t mExternalStylusId; - nsecs_t mExternalStylusFusionTimeout; - bool mExternalStylusDataPending; - - // True if we sent a HOVER_ENTER event. - bool mSentHoverEnter; - - // Have we assigned pointer IDs for this stream - bool mHavePointerIds; - - // Is the current stream of direct touch events aborted - bool mCurrentMotionAborted; - - // The time the primary pointer last went down. - nsecs_t mDownTime; - - // The pointer controller, or null if the device is not a pointer. - sp mPointerController; - - std::vector mVirtualKeys; - - virtual void configureParameters(); - virtual void dumpParameters(std::string& dump); - virtual void configureRawPointerAxes(); - virtual void dumpRawPointerAxes(std::string& dump); - virtual void configureSurface(nsecs_t when, bool* outResetNeeded); - virtual void dumpSurface(std::string& dump); - virtual void configureVirtualKeys(); - virtual void dumpVirtualKeys(std::string& dump); - virtual void parseCalibration(); - virtual void resolveCalibration(); - virtual void dumpCalibration(std::string& dump); - virtual void updateAffineTransformation(); - virtual void dumpAffineTransformation(std::string& dump); - virtual void resolveExternalStylusPresence(); - virtual bool hasStylus() const = 0; - virtual bool hasExternalStylus() const; - - virtual void syncTouch(nsecs_t when, RawState* outState) = 0; - -private: - // The current viewport. - // The components of the viewport are specified in the display's rotated orientation. - DisplayViewport mViewport; - - // The surface orientation, width and height set by configureSurface(). - // The width and height are derived from the viewport but are specified - // in the natural orientation. - // The surface origin specifies how the surface coordinates should be translated - // to align with the logical display coordinate space. - int32_t mSurfaceWidth; - int32_t mSurfaceHeight; - int32_t mSurfaceLeft; - int32_t mSurfaceTop; - - // Similar to the surface coordinates, but in the raw display coordinate space rather than in - // the logical coordinate space. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; - - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. - int32_t mSurfaceOrientation; - - // Translation and scaling factors, orientation-independent. - float mXTranslate; - float mXScale; - float mXPrecision; - - float mYTranslate; - float mYScale; - float mYPrecision; - - float mGeometricScale; - - float mPressureScale; - - float mSizeScale; - - float mOrientationScale; - - float mDistanceScale; - - bool mHaveTilt; - float mTiltXCenter; - float mTiltXScale; - float mTiltYCenter; - float mTiltYScale; - - bool mExternalStylusConnected; - - // Oriented motion ranges for input device info. - struct OrientedRanges { - InputDeviceInfo::MotionRange x; - InputDeviceInfo::MotionRange y; - InputDeviceInfo::MotionRange pressure; - - bool haveSize; - InputDeviceInfo::MotionRange size; - - bool haveTouchSize; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; - - bool haveToolSize; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; - - bool haveOrientation; - InputDeviceInfo::MotionRange orientation; - - bool haveDistance; - InputDeviceInfo::MotionRange distance; - - bool haveTilt; - InputDeviceInfo::MotionRange tilt; - - OrientedRanges() { clear(); } - - void clear() { - haveSize = false; - haveTouchSize = false; - haveToolSize = false; - haveOrientation = false; - haveDistance = false; - haveTilt = false; - } - } mOrientedRanges; - - // Oriented dimensions and precision. - float mOrientedXPrecision; - float mOrientedYPrecision; - - struct CurrentVirtualKeyState { - bool down; - bool ignored; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } mCurrentVirtualKey; - - // Scale factor for gesture or mouse based pointer movements. - float mPointerXMovementScale; - float mPointerYMovementScale; - - // Scale factor for gesture based zooming and other freeform motions. - float mPointerXZoomScale; - float mPointerYZoomScale; - - // The maximum swipe width. - float mPointerGestureMaxSwipeWidth; - - struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance - }; - - enum PointerUsage { - POINTER_USAGE_NONE, - POINTER_USAGE_GESTURES, - POINTER_USAGE_STYLUS, - POINTER_USAGE_MOUSE, - }; - PointerUsage mPointerUsage; - - struct PointerGesture { - enum Mode { - // No fingers, button is not pressed. - // Nothing happening. - NEUTRAL, - - // No fingers, button is not pressed. - // Tap detected. - // Emits DOWN and UP events at the pointer location. - TAP, - - // Exactly one finger dragging following a tap. - // Pointer follows the active finger. - // Emits DOWN, MOVE and UP events at the pointer location. - // - // Detect double-taps when the finger goes up while in TAP_DRAG mode. - TAP_DRAG, - - // Button is pressed. - // Pointer follows the active finger if there is one. Other fingers are ignored. - // Emits DOWN, MOVE and UP events at the pointer location. - BUTTON_CLICK_OR_DRAG, - - // Exactly one finger, button is not pressed. - // Pointer follows the active finger. - // Emits HOVER_MOVE events at the pointer location. - // - // Detect taps when the finger goes up while in HOVER mode. - HOVER, - - // Exactly two fingers but neither have moved enough to clearly indicate - // whether a swipe or freeform gesture was intended. We consider the - // pointer to be pressed so this enables clicking or long-pressing on buttons. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. - PRESS, - - // Exactly two fingers moving in the same direction, button is not pressed. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single pointer coordinate that - // follows the midpoint between both fingers. - SWIPE, - - // Two or more fingers moving in arbitrary directions, button is not pressed. - // Pointer does not move. - // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow - // each finger individually relative to the initial centroid of the finger. - FREEFORM, - - // Waiting for quiet time to end before starting the next gesture. - QUIET, - }; - - // Time the first finger went down. - nsecs_t firstTouchTime; - - // The active pointer id from the raw touch data. - int32_t activeTouchId; // -1 if none - - // The active pointer id from the gesture last delivered to the application. - int32_t activeGestureId; // -1 if none - - // Pointer coords and ids for the current and previous pointer gesture. - Mode currentGestureMode; - BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; - - Mode lastGestureMode; - BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; - - // Time the pointer gesture last went down. - nsecs_t downTime; - - // Time when the pointer went down for a TAP. - nsecs_t tapDownTime; - - // Time when the pointer went up for a TAP. - nsecs_t tapUpTime; - - // Location of initial tap. - float tapX, tapY; - - // Time we started waiting for quiescence. - nsecs_t quietTime; - - // Reference points for multitouch gestures. - float referenceTouchX; // reference touch X/Y coordinates in surface units - float referenceTouchY; - float referenceGestureX; // reference gesture X/Y coordinates in pixels - float referenceGestureY; - - // Distance that each pointer has traveled which has not yet been - // subsumed into the reference gesture position. - BitSet32 referenceIdBits; - struct Delta { - float dx, dy; - }; - Delta referenceDeltas[MAX_POINTER_ID + 1]; - - // Describes how touch ids are mapped to gesture ids for freeform gestures. - uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; - - // A velocity tracker for determining whether to switch active pointers during drags. - VelocityTracker velocityTracker; - - void reset() { - firstTouchTime = LLONG_MIN; - activeTouchId = -1; - activeGestureId = -1; - currentGestureMode = NEUTRAL; - currentGestureIdBits.clear(); - lastGestureMode = NEUTRAL; - lastGestureIdBits.clear(); - downTime = 0; - velocityTracker.clear(); - resetTap(); - resetQuietTime(); - } - - void resetTap() { - tapDownTime = LLONG_MIN; - tapUpTime = LLONG_MIN; - } - - void resetQuietTime() { quietTime = LLONG_MIN; } - } mPointerGesture; - - struct PointerSimple { - PointerCoords currentCoords; - PointerProperties currentProperties; - PointerCoords lastCoords; - PointerProperties lastProperties; - - // True if the pointer is down. - bool down; - - // True if the pointer is hovering. - bool hovering; - - // Time the pointer last went down. - nsecs_t downTime; - - void reset() { - currentCoords.clear(); - currentProperties.clear(); - lastCoords.clear(); - lastProperties.clear(); - down = false; - hovering = false; - downTime = 0; - } - } mPointerSimple; - - // The pointer and scroll velocity controls. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - std::optional findViewport(); - - void resetExternalStylus(); - void clearStylusDataPendingFlags(); - - void sync(nsecs_t when); - - bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); - void processRawTouches(bool timeout); - void cookAndDispatch(nsecs_t when); - void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, - int32_t keyEventFlags); - - void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); - void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); - void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); - void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); - const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); - void cookPointerData(); - void abortTouches(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); - void abortPointerUsage(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); - void abortPointerGestures(nsecs_t when, uint32_t policyFlags); - bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, - bool* outFinishPreviousGesture, bool isTimeout); - - void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); - void abortPointerStylus(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); - void abortPointerMouse(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); - void abortPointerSimple(nsecs_t when, uint32_t policyFlags); - - bool assignExternalStylusId(const RawState& state, bool timeout); - void applyExternalStylusButtonState(nsecs_t when); - void applyExternalStylusTouchState(nsecs_t when); - - // Dispatches a motion event. - // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the - // method will take care of setting the index and transmuting the action to DOWN or UP - // it is the first / last pointer to go down / up. - void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, - int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, const PointerProperties* properties, - const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, - int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); - - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, - const uint32_t* inIdToIndex, PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const; - - bool isPointInsideSurface(int32_t x, int32_t y); - const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); - - static void assignPointerIds(const RawState* last, RawState* current); - - const char* modeToString(DeviceMode deviceMode); -}; - -} // namespace android - -#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp new file mode 100644 index 0000000000..f69138ea09 --- /dev/null +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "CursorInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +namespace android { + +// --- CursorMotionAccumulator --- + +CursorMotionAccumulator::CursorMotionAccumulator() { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::clearRelativeAxes() { + mRelX = 0; + mRelY = 0; +} + +void CursorMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_X: + mRelX = rawEvent->value; + break; + case REL_Y: + mRelY = rawEvent->value; + break; + } + } +} + +void CursorMotionAccumulator::finishSync() { + clearRelativeAxes(); +} + +// --- CursorInputMapper --- + +CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} + +CursorInputMapper::~CursorInputMapper() {} + +uint32_t CursorInputMapper::getSources() { + return mSource; +} + +void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mParameters.mode == Parameters::MODE_POINTER) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); + } + } else { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } +} + +void CursorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Cursor Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeVWheel())); + dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); + dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); + dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); + dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + mCursorScrollAccumulator.configure(getDevice()); + + // Configure basic parameters. + configureParameters(); + + // Configure device mode. + switch (mParameters.mode) { + case Parameters::MODE_POINTER_RELATIVE: + // Should not happen during first time configuration. + ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); + mParameters.mode = Parameters::MODE_POINTER; + [[fallthrough]]; + case Parameters::MODE_POINTER: + mSource = AINPUT_SOURCE_MOUSE; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + mXScale = 1.0f; + mYScale = 1.0f; + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + break; + case Parameters::MODE_NAVIGATION: + mSource = AINPUT_SOURCE_TRACKBALL; + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + break; + } + + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; + } + + if ((!changes && config->pointerCapture) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { + if (config->pointerCapture) { + if (mParameters.mode == Parameters::MODE_POINTER) { + mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + mSource = AINPUT_SOURCE_MOUSE_RELATIVE; + // Keep PointerController around in order to preserve the pointer position. + mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else { + ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); + } + } else { + if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { + mParameters.mode = Parameters::MODE_POINTER; + mSource = AINPUT_SOURCE_MOUSE; + } else { + ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); + } + } + bumpGeneration(); + if (changes) { + getDevice()->notifyReset(when); + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mOrientation = DISPLAY_ORIENTATION_0; + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } + } + + // Update the PointerController if viewports changed. + if (mParameters.mode == Parameters::MODE_POINTER) { + getPolicy()->obtainPointerController(getDeviceId()); + } + bumpGeneration(); + } +} + +void CursorInputMapper::configureParameters() { + mParameters.mode = Parameters::MODE_POINTER; + String8 cursorModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { + if (cursorModeString == "navigation") { + mParameters.mode = Parameters::MODE_NAVIGATION; + } else if (cursorModeString != "pointer" && cursorModeString != "default") { + ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); + } + } + + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + mParameters.hasAssociatedDisplay = true; + } +} + +void CursorInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); + + switch (mParameters.mode) { + case Parameters::MODE_POINTER: + dump += INDENT4 "Mode: pointer\n"; + break; + case Parameters::MODE_POINTER_RELATIVE: + dump += INDENT4 "Mode: relative pointer\n"; + break; + case Parameters::MODE_NAVIGATION: + dump += INDENT4 "Mode: navigation\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void CursorInputMapper::reset(nsecs_t when) { + mButtonState = 0; + mDownTime = 0; + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mCursorButtonAccumulator.reset(getDevice()); + mCursorMotionAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void CursorInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorMotionAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void CursorInputMapper::sync(nsecs_t when) { + int32_t lastButtonState = mButtonState; + int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); + mButtonState = currentButtonState; + + bool wasDown = isPointerDown(lastButtonState); + bool down = isPointerDown(currentButtonState); + bool downChanged; + if (!wasDown && down) { + mDownTime = when; + downChanged = true; + } else if (wasDown && !down) { + downChanged = true; + } else { + downChanged = false; + } + nsecs_t downTime = mDownTime; + bool buttonsChanged = currentButtonState != lastButtonState; + int32_t buttonsPressed = currentButtonState & ~lastButtonState; + int32_t buttonsReleased = lastButtonState & ~currentButtonState; + + float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; + float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; + bool moved = deltaX != 0 || deltaY != 0; + + // Rotate delta according to orientation if needed. + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && + (deltaX != 0.0f || deltaY != 0.0f)) { + rotateDelta(mOrientation, &deltaX, &deltaY); + } + + // Move the pointer. + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); + float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); + bool scrolled = vscroll != 0 || hscroll != 0; + + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mSource == AINPUT_SOURCE_MOUSE) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + + if (buttonsChanged) { + mPointerController->setButtonState(currentButtonState); + } + + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); + displayId = mPointerController->getDisplayId(); + } else { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); + displayId = ADISPLAY_ID_NONE; + } + + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); + + // Moving an external trackball or mouse should wake the device. + // We don't do this for internal cursor devices to prevent them from waking up + // the device in your pocket. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + // Synthesize key down from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + // Send motion event. + if (downChanged || moved || scrolled || buttonsChanged) { + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = lastButtonState; + int32_t motionEventAction; + if (downChanged) { + motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } else { + motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; + } + + if (buttonsReleased) { + BitSet32 released(buttonsReleased); + while (!released.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); + buttonState &= ~actionButton; + NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&releaseArgs); + } + } + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + + if (buttonsPressed) { + BitSet32 pressed(buttonsPressed); + while (!pressed.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); + buttonState |= actionButton; + NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&pressArgs); + } + } + + ALOG_ASSERT(buttonState == currentButtonState); + + // Send hover move after UP to tell the application that the mouse is hovering now. + if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&hoverArgs); + } + + // Send scroll events. + if (scrolled) { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + } + + // Synthesize key up from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + mCursorMotionAccumulator.finishSync(); + mCursorScrollAccumulator.finishSync(); +} + +int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + +void CursorInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +std::optional CursorInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mParameters.mode == Parameters::MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + // If the device is orientationAware and not a mouse, + // it expects to dispatch events to any display + return std::make_optional(ADISPLAY_ID_NONE); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h new file mode 100644 index 0000000000..52e42a7991 --- /dev/null +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +#include +#include + +namespace android { + +class VelocityControl; +class PointerControllerInterface; + +class CursorButtonAccumulator; +class CursorScrollAccumulator; + +/* Keeps track of cursor movements. */ +class CursorMotionAccumulator { +public: + CursorMotionAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + +private: + int32_t mRelX; + int32_t mRelY; + + void clearRelativeAxes(); +}; + +class CursorInputMapper : public InputMapper { +public: + explicit CursorInputMapper(InputDevice* device); + virtual ~CursorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + + virtual void fadePointer(); + + virtual std::optional getAssociatedDisplayId(); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + // Immutable configuration parameters. + struct Parameters { + enum Mode { + MODE_POINTER, + MODE_POINTER_RELATIVE, + MODE_NAVIGATION, + }; + + Mode mode; + bool hasAssociatedDisplay; + bool orientationAware; + } mParameters; + + CursorButtonAccumulator mCursorButtonAccumulator; + CursorMotionAccumulator mCursorMotionAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + + int32_t mSource; + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + float mVWheelScale; + float mHWheelScale; + + // Velocity controls for mouse pointer and wheel movements. + // The controls for X and Y wheel movements are separate to keep them decoupled. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + int32_t mOrientation; + + sp mPointerController; + + int32_t mButtonState; + nsecs_t mDownTime; + + void configureParameters(); + void dumpParameters(std::string& dump); + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp new file mode 100644 index 0000000000..9aa0770245 --- /dev/null +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "ExternalStylusInputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} + +uint32_t ExternalStylusInputMapper::getSources() { + return AINPUT_SOURCE_STYLUS; +} + +void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); +} + +void ExternalStylusInputMapper::dump(std::string& dump) { + dump += INDENT2 "External Stylus Input Mapper:\n"; + dump += INDENT3 "Raw Stylus Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); + dump += INDENT3 "Stylus State:\n"; + dumpStylusState(dump, mStylusState); +} + +void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); + mTouchButtonAccumulator.configure(getDevice()); +} + +void ExternalStylusInputMapper::reset(nsecs_t when) { + InputDevice* device = getDevice(); + mSingleTouchMotionAccumulator.reset(device); + mTouchButtonAccumulator.reset(device); + InputMapper::reset(when); +} + +void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { + mSingleTouchMotionAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void ExternalStylusInputMapper::sync(nsecs_t when) { + mStylusState.clear(); + + mStylusState.when = when; + + mStylusState.toolType = mTouchButtonAccumulator.getToolType(); + if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + + int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + if (mRawPressureAxis.valid) { + mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; + } else if (mTouchButtonAccumulator.isToolActive()) { + mStylusState.pressure = 1.0f; + } else { + mStylusState.pressure = 0.0f; + } + + mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); + + mContext->dispatchExternalStylusState(mStylusState); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h new file mode 100644 index 0000000000..9764fbb3c1 --- /dev/null +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H +#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H + +#include "InputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "StylusState.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +class ExternalStylusInputMapper : public InputMapper { +public: + explicit ExternalStylusInputMapper(InputDevice* device); + virtual ~ExternalStylusInputMapper() = default; + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + virtual void sync(nsecs_t when); + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; + RawAbsoluteAxisInfo mRawPressureAxis; + TouchButtonAccumulator mTouchButtonAccumulator; + + StylusState mStylusState; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp new file mode 100644 index 0000000000..d941528d14 --- /dev/null +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputMapper.h" + +#include "InputDevice.h" + +namespace android { + +InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} + +InputMapper::~InputMapper() {} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::dump(std::string& dump) {} + +void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) {} + +void InputMapper::reset(nsecs_t when) {} + +void InputMapper::timeoutExpired(nsecs_t when) {} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) {} + +void InputMapper::cancelVibrate(int32_t token) {} + +void InputMapper::cancelTouch(nsecs_t when) {} + +int32_t InputMapper::getMetaState() { + return 0; +} + +void InputMapper::updateMetaState(int32_t keyCode) {} + +void InputMapper::updateExternalStylusState(const StylusState& state) {} + +void InputMapper::fadePointer() {} + +status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { + return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); +} + +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + +void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name) { + if (axis.valid) { + dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, + axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); + } else { + dump += StringPrintf(INDENT4 "%s: unknown range\n", name); + } +} + +void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { + dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); + dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); + dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h new file mode 100644 index 0000000000..a559ef882f --- /dev/null +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_MAPPER_H +#define _UI_INPUTREADER_INPUT_MAPPER_H + +#include "EventHub.h" +#include "InputDevice.h" +#include "InputListener.h" +#include "InputReaderContext.h" +#include "StylusState.h" + +namespace android { + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + * + * InputMapper lifecycle: + * - create + * - configure with 0 changes + * - reset + * - process, process, process (may occasionally reconfigure with non-zero changes or reset) + * - reset + * - destroy + */ +class InputMapper { +public: + explicit InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const std::string getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputListenerInterface* getListener() { return mContext->getListener(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent) = 0; + virtual void timeoutExpired(nsecs_t when); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void cancelTouch(nsecs_t when); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + + virtual void updateExternalStylusState(const StylusState& state); + + virtual void fadePointer(); + virtual std::optional getAssociatedDisplayId() { return std::nullopt; } + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); + + static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name); + static void dumpStylusState(std::string& dump, const StylusState& state); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp new file mode 100644 index 0000000000..50adf73c76 --- /dev/null +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "JoystickInputMapper.h" + +namespace android { + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} + +JoystickInputMapper::~JoystickInputMapper() {} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (size_t i = 0; i < mAxes.size(); i++) { + const Axis& axis = mAxes.valueAt(i); + addMotionRange(axis.axisInfo.axis, axis, info); + + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + addMotionRange(axis.axisInfo.highAxis, axis, info); + } + } +} + +void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { + info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, + axis.resolution); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to register + * the old axes as duplicates of their corresponding new ones. */ + int32_t compatAxis = getCompatAxis(axisId); + if (compatAxis >= 0) { + info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, + axis.fuzz, axis.resolution); + } +} + +/* A mapping from axes the joystick actually has to the axes that should be + * artificially created for compatibility purposes. + * Returns -1 if no compatibility axis is needed. */ +int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_LTRIGGER: + return AMOTION_EVENT_AXIS_BRAKE; + case AMOTION_EVENT_AXIS_RTRIGGER: + return AMOTION_EVENT_AXIS_GAS; + } + return -1; +} + +void JoystickInputMapper::dump(std::string& dump) { + dump += INDENT2 "Joystick Input Mapper:\n"; + + dump += INDENT3 "Axes:\n"; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + const char* label = getAxisLabel(axis.axisInfo.axis); + if (label) { + dump += StringPrintf(INDENT4 "%s", label); + } else { + dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + label = getAxisLabel(axis.axisInfo.highAxis); + if (label) { + dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); + } else { + dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, + axis.axisInfo.splitValue); + } + } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { + dump += " (invert)"; + } + + dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " + "highScale=%0.5f, highOffset=%0.5f\n", + axis.scale, axis.offset, axis.highScale, axis.highOffset); + dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", + mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, + axis.rawAxisInfo.resolution); + } +} + +void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { + continue; // axis must be claimed by a different device + } + + RawAbsoluteAxisInfo rawAxisInfo; + getAbsoluteAxisInfo(abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + // Map axis. + AxisInfo axisInfo; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + } + + // Apply flat override. + int32_t rawFlat = + axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + // Calculate scaling factors and limits. + Axis axis; + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else if (isCenteredAxis(axisInfo.axis)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, + offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; + + mAxes.add(abs, axis); + } + } + + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", + getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axisInfo.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && + haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } + + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axisInfo.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } + } + } + } +} + +bool JoystickInputMapper::haveAxis(int32_t axisId) { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + if (axis.axisInfo.axis == axisId || + (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { + return true; + } + } + return false; +} + +void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { + size_t i = mAxes.size(); + while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { + if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + continue; + } + ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i); + } +} + +bool JoystickInputMapper::isCenteredAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_Z: + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + case AMOTION_EVENT_AXIS_HAT_X: + case AMOTION_EVENT_AXIS_HAT_Y: + case AMOTION_EVENT_AXIS_ORIENTATION: + case AMOTION_EVENT_AXIS_RUDDER: + case AMOTION_EVENT_AXIS_WHEEL: + return true; + default: + return false; + } +} + +void JoystickInputMapper::reset(nsecs_t when) { + // Recenter all axes. + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + axis.resetValue(); + } + + InputMapper::reset(when); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + ssize_t index = mAxes.indexOfKey(rawEvent->code); + if (index >= 0) { + Axis& axis = mAxes.editValueAt(index); + float newValue, highNewValue; + switch (axis.axisInfo.mode) { + case AxisInfo::MODE_INVERT: + newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + break; + case AxisInfo::MODE_SPLIT: + if (rawEvent->value < axis.axisInfo.splitValue) { + newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + } else if (rawEvent->value > axis.axisInfo.splitValue) { + newValue = 0.0f; + highNewValue = + (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + + axis.highOffset; + } else { + newValue = 0.0f; + highNewValue = 0.0f; + } + break; + default: + newValue = rawEvent->value * axis.scale + axis.offset; + highNewValue = 0.0f; + break; + } + axis.newValue = newValue; + axis.highNewValue = highNewValue; + } + break; + } + + case EV_SYN: + switch (rawEvent->code) { + case SYN_REPORT: + sync(rawEvent->when, false /*force*/); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when, bool force) { + if (!filterAxes(force)) { + return; + } + + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = 0; + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, + axis.highCurrentValue); + } + } + + // Moving a joystick axis should not wake the device because joysticks can + // be fairly noisy even when not in use. On the other hand, pushing a gamepad + // button will likely wake the device. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&args); +} + +void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, + float value) { + pointerCoords->setAxisValue(axis, value); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to produce + * values for the old axes as mirrors of the value of their corresponding + * new axes. */ + int32_t compatAxis = getCompatAxis(axis); + if (compatAxis >= 0) { + pointerCoords->setAxisValue(compatAxis, value); + } +} + +bool JoystickInputMapper::filterAxes(bool force) { + bool atLeastOneSignificantChange = force; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (force || + hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, + axis.max)) { + axis.currentValue = axis.newValue; + atLeastOneSignificantChange = true; + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + if (force || + hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, + axis.min, axis.max)) { + axis.highCurrentValue = axis.highNewValue; + atLeastOneSignificantChange = true; + } + } + } + return atLeastOneSignificantChange; +} + +bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, + float currentValue, float min, float max) { + if (newValue != currentValue) { + // Filter out small changes in value unless the value is converging on the axis + // bounds or center point. This is intended to reduce the amount of information + // sent to applications by particularly noisy joysticks (such as PS3). + if (fabs(newValue - currentValue) > filter || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { + return true; + } + } + return false; +} + +bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, + float thresholdValue) { + float newDistance = fabs(newValue - thresholdValue); + if (newDistance < filter) { + float oldDistance = fabs(currentValue - thresholdValue); + if (newDistance < oldDistance) { + return true; + } + } + return false; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h new file mode 100644 index 0000000000..1b071d0480 --- /dev/null +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H +#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class JoystickInputMapper : public InputMapper { +public: + explicit JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + struct Axis { + RawAbsoluteAxisInfo rawAxisInfo; + AxisInfo axisInfo; + + bool explicitlyMapped; // true if the axis was explicitly assigned an axis id + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + float highScale; // scale factor from raw to normalized values of high split + float highOffset; // offset to add after scaling for normalization of high split + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units/mm + + float filter; // filter out small variations of this size + float currentValue; // current value + float newValue; // most recent value + float highCurrentValue; // current value of high split + float highNewValue; // most recent value of high split + + void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, + bool explicitlyMapped, float scale, float offset, float highScale, + float highOffset, float min, float max, float flat, float fuzz, + float resolution) { + this->rawAxisInfo = rawAxisInfo; + this->axisInfo = axisInfo; + this->explicitlyMapped = explicitlyMapped; + this->scale = scale; + this->offset = offset; + this->highScale = highScale; + this->highOffset = highOffset; + this->min = min; + this->max = max; + this->flat = flat; + this->fuzz = fuzz; + this->resolution = resolution; + this->filter = 0; + resetValue(); + } + + void resetValue() { + this->currentValue = 0; + this->newValue = 0; + this->highCurrentValue = 0; + this->highNewValue = 0; + } + }; + + // Axes indexed by raw ABS_* axis index. + KeyedVector mAxes; + + void sync(nsecs_t when, bool force); + + bool haveAxis(int32_t axisId); + void pruneAxes(bool ignoreExplicitlyMappedAxes); + bool filterAxes(bool force); + + static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, + float min, float max); + static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, float thresholdValue); + + static bool isCenteredAxis(int32_t axis); + static int32_t getCompatAxis(int32_t axis); + + static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); + static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp new file mode 100644 index 0000000000..f51d4a0d4a --- /dev/null +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "KeyboardInputMapper.h" + +namespace android { + +// --- Static Definitions --- + +static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, + const int32_t map[][4], size_t mapSize) { + if (orientation != DISPLAY_ORIENTATION_0) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][orientation]; + } + } + } + return value; +} + +static const int32_t keyCodeRotationMap[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, +}; + +static const size_t keyCodeRotationMapSize = + sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); + +static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], + size_t mapSize) { + if (orientation == DISPLAY_ORIENTATION_180) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][1]; + } + } + } + return value; +} + +// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X +static int32_t stemKeyRotationMap[][2] = { + // key codes enumerated with the original (unrotated) key first + // no rotation, 180 degree rotation + {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, + {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, + {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, + {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, +}; + +static const size_t stemKeyRotationMapSize = + sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); + +static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); + return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, + keyCodeRotationMapSize); +} + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) + : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} + +KeyboardInputMapper::~KeyboardInputMapper() {} + +uint32_t KeyboardInputMapper::getSources() { + return mSource; +} + +int32_t KeyboardInputMapper::getOrientation() { + if (mViewport) { + return mViewport->orientation; + } + return DISPLAY_ORIENTATION_0; +} + +int32_t KeyboardInputMapper::getDisplayId() { + if (mViewport) { + return mViewport->displayId; + } + return ADISPLAY_ID_NONE; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); +} + +void KeyboardInputMapper::dump(std::string& dump) { + dump += INDENT2 "Keyboard Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); + dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); + dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +std::optional KeyboardInputMapper::findViewport( + nsecs_t when, const InputReaderConfiguration* config) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // No associated display defined, try to find default display if orientationAware. + if (mParameters.orientationAware) { + return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return std::nullopt; +} + +void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mViewport = findViewport(when, config); + } +} + +static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { + int32_t mapped = 0; + if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { + for (size_t i = 0; i < stemKeyRotationMapSize; i++) { + if (stemKeyRotationMap[i][0] == keyCode) { + stemKeyRotationMap[i][1] = mapped; + return; + } + } + } +} + +void KeyboardInputMapper::configureParameters() { + mParameters.orientationAware = false; + const PropertyMap& config = getDevice()->getConfiguration(); + config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); + + if (mParameters.orientationAware) { + mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); + mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); + mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); + mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); + } + + mParameters.handlesKeyRepeat = false; + config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); +} + +void KeyboardInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); + dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); +} + +void KeyboardInputMapper::reset(nsecs_t when) { + mMetaState = AMETA_NONE; + mDownTime = 0; + mKeyDowns.clear(); + mCurrentHidUsage = 0; + + resetLedState(); + + InputMapper::reset(when); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->code; + int32_t usageCode = mCurrentHidUsage; + mCurrentHidUsage = 0; + + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); + } + break; + } + case EV_MSC: { + if (rawEvent->code == MSC_SCAN) { + mCurrentHidUsage = rawEvent->value; + } + break; + } + case EV_SYN: { + if (rawEvent->code == SYN_REPORT) { + mCurrentHidUsage = 0; + } + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE || scanCode >= KEY_OK || + (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || + (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); +} + +bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + } + return false; +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { + int32_t keyCode; + int32_t keyMetaState; + uint32_t policyFlags; + + if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, + &keyMetaState, &policyFlags)) { + keyCode = AKEYCODE_UNKNOWN; + keyMetaState = mMetaState; + policyFlags = 0; + } + + if (down) { + // Rotate key codes according to orientation if needed. + if (mParameters.orientationAware) { + keyCode = rotateKeyCode(keyCode, getOrientation()); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + } else { + // key down + if ((policyFlags & POLICY_FLAG_VIRTUAL) && + mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { + return; + } + if (policyFlags & POLICY_FLAG_GESTURE) { + mDevice->cancelTouch(when); + } + + KeyDown keyDown; + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + mKeyDowns.push_back(keyDown); + } + + mDownTime = when; + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + } else { + // key was not actually down + ALOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().c_str(), keyCode, scanCode); + return; + } + } + + if (updateMetaStateIfNeeded(keyCode, down)) { + // If global meta state changed send it along with the key. + // If it has not changed then we'll use what keymap gave us, + // since key replacement logic might temporarily reset a few + // meta bits for given key. + keyMetaState = mMetaState; + } + + nsecs_t downTime = mDownTime; + + // Key down on external an keyboard should wake the device. + // We don't do this for internal keyboards to prevent them from waking up in your pocket. + // For internal keyboards, the key layout file should specify the policy flags for + // each wake key individually. + // TODO: Use the input device configuration to control this behavior more finely. + if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mParameters.handlesKeyRepeat) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + getListener()->notifyKey(&args); +} + +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} + +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} + +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} + +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + +void KeyboardInputMapper::updateMetaState(int32_t keyCode) { + updateMetaStateIfNeeded(keyCode, false); +} + +bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { + int32_t oldMetaState = mMetaState; + int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); + bool metaStateChanged = oldMetaState != newMetaState; + if (metaStateChanged) { + mMetaState = newMetaState; + updateLedState(false); + + getContext()->updateGlobalMetaState(); + } + + return metaStateChanged; +} + +void KeyboardInputMapper::resetLedState() { + initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); + initializeLedState(mNumLockLedState, ALED_NUM_LOCK); + initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); + + updateLedState(true); +} + +void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { + ledState.avail = getEventHub()->hasLed(getDeviceId(), led); + ledState.on = false; +} + +void KeyboardInputMapper::updateLedState(bool reset) { + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); + updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); + updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); +} + +void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, + int32_t modifier, bool reset) { + if (ledState.avail) { + bool desiredState = (mMetaState & modifier) != 0; + if (reset || ledState.on != desiredState) { + getEventHub()->setLedState(getDeviceId(), led, desiredState); + ledState.on = desiredState; + } + } +} + +std::optional KeyboardInputMapper::getAssociatedDisplayId() { + if (mViewport) { + return std::make_optional(mViewport->displayId); + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h new file mode 100644 index 0000000000..2665c0258c --- /dev/null +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H +#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + virtual std::optional getAssociatedDisplayId(); + +private: + // The current viewport. + std::optional mViewport; + + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + uint32_t mSource; + int32_t mKeyboardType; + + std::vector mKeyDowns; // keys that are down + int32_t mMetaState; + nsecs_t mDownTime; // time of most recent key down + + int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + + struct LedState { + bool avail; // led is available + bool on; // we think the led is currently on + }; + LedState mCapsLockLedState; + LedState mNumLockLedState; + LedState mScrollLockLedState; + + // Immutable configuration parameters. + struct Parameters { + bool orientationAware; + bool handlesKeyRepeat; + } mParameters; + + void configureParameters(); + void dumpParameters(std::string& dump); + + int32_t getOrientation(); + int32_t getDisplayId(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + bool isMediaKey(int32_t keyCode); + + void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); + + bool updateMetaStateIfNeeded(int32_t keyCode, bool down); + + ssize_t findKeyDown(int32_t scanCode); + + void resetLedState(); + void initializeLedState(LedState& ledState, int32_t led); + void updateLedState(bool reset); + void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); + std::optional findViewport(nsecs_t when, + const InputReaderConfiguration* config); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp new file mode 100644 index 0000000000..c567c8bf80 --- /dev/null +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "MultiTouchInputMapper.h" + +namespace android { + +// --- Constants --- + +// Maximum number of slots supported when using the slot-based Multitouch Protocol B. +static constexpr size_t MAX_SLOTS = 32; + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), + mSlots(nullptr), + mSlotCount(0), + mUsingSlotsProtocol(false), + mHaveStylus(false) {} + +MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { + delete[] mSlots; +} + +void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, + bool usingSlotsProtocol) { + mSlotCount = slotCount; + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + + delete[] mSlots; + mSlots = new Slot[slotCount]; +} + +void MultiTouchMotionAccumulator::reset(InputDevice* device) { + // Unfortunately there is no way to read the initial contents of the slots. + // So when we reset the accumulator, we must assume they are all zeroes. + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot + // before we start reading events from the device. It is possible that the + // current slot index will not be the same as it was when the first event was + // written into the evdev buffer, which means the input mapper could start + // out of sync with the initial state of the events in the evdev buffer. + // In the extremely unlikely case that this happens, the data from + // two slots will be confused until the next ABS_MT_SLOT event is received. + // This can cause the touch point to "jump", but at least there will be + // no stuck touches. + int32_t initialSlot; + status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, + &initialSlot); + if (status) { + ALOGD("Could not retrieve current multitouch slot index. status=%d", status); + initialSlot = -1; + } + clearSlots(initialSlot); + } else { + clearSlots(-1); + } +} + +void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { + if (mSlots) { + for (size_t i = 0; i < mSlotCount; i++) { + mSlots[i].clear(); + } + } + mCurrentSlot = initialSlot; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { +#if DEBUG_POINTERS + if (newSlot) { + ALOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } +#endif + } else { + Slot* slot = &mSlots[mCurrentSlot]; + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot->mInUse = true; + slot->mAbsMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + slot->mInUse = true; + slot->mAbsMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + slot->mInUse = true; + slot->mAbsMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot->mInUse = true; + slot->mAbsMTTouchMinor = rawEvent->value; + slot->mHaveAbsMTTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot->mInUse = true; + slot->mAbsMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot->mInUse = true; + slot->mAbsMTWidthMinor = rawEvent->value; + slot->mHaveAbsMTWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot->mInUse = true; + slot->mAbsMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot->mInUse = false; + } else { + slot->mInUse = true; + slot->mAbsMTTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot->mInUse = true; + slot->mAbsMTPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot->mInUse = true; + slot->mAbsMTDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->mInUse = true; + slot->mAbsMTToolType = rawEvent->value; + slot->mHaveAbsMTToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + clearSlots(-1); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +// --- MultiTouchMotionAccumulator::Slot --- + +MultiTouchMotionAccumulator::Slot::Slot() { + clear(); +} + +void MultiTouchMotionAccumulator::Slot::clear() { + mInUse = false; + mHaveAbsMTTouchMinor = false; + mHaveAbsMTWidthMinor = false; + mHaveAbsMTToolType = false; + mAbsMTPositionX = 0; + mAbsMTPositionY = 0; + mAbsMTTouchMajor = 0; + mAbsMTTouchMinor = 0; + mAbsMTWidthMajor = 0; + mAbsMTWidthMinor = 0; + mAbsMTOrientation = 0; + mAbsMTTrackingId = -1; + mAbsMTPressure = 0; + mAbsMTDistance = 0; + mAbsMTToolType = 0; +} + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMTToolType) { + switch (mAbsMTToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +MultiTouchInputMapper::~MultiTouchInputMapper() {} + +void MultiTouchInputMapper::reset(nsecs_t when) { + mMultiTouchMotionAccumulator.reset(getDevice()); + + mPointerIdBits.clear(); + + TouchInputMapper::reset(when); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mMultiTouchMotionAccumulator.process(rawEvent); +} + +void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); + size_t outCount = 0; + BitSet32 newPointerIdBits; + mHavePointerIds = true; + + for (size_t inIndex = 0; inIndex < inCount; inIndex++) { + const MultiTouchMotionAccumulator::Slot* inSlot = + mMultiTouchMotionAccumulator.getSlot(inIndex); + if (!inSlot->isInUse()) { + continue; + } + + if (outCount >= MAX_POINTERS) { +#if DEBUG_POINTERS + ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().c_str(), MAX_POINTERS); +#endif + break; // too many fingers! + } + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; + outPointer.x = inSlot->getX(); + outPointer.y = inSlot->getY(); + outPointer.pressure = inSlot->getPressure(); + outPointer.touchMajor = inSlot->getTouchMajor(); + outPointer.touchMinor = inSlot->getTouchMinor(); + outPointer.toolMajor = inSlot->getToolMajor(); + outPointer.toolMinor = inSlot->getToolMinor(); + outPointer.orientation = inSlot->getOrientation(); + outPointer.distance = inSlot->getDistance(); + outPointer.tiltX = 0; + outPointer.tiltY = 0; + + outPointer.toolType = inSlot->getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + } + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); + outPointer.isHovering = isHovering; + + // Assign pointer id using tracking id if available. + if (mHavePointerIds) { + int32_t trackingId = inSlot->getTrackingId(); + int32_t id = -1; + if (trackingId >= 0) { + for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { + uint32_t n = idBits.clearFirstMarkedBit(); + if (mPointerTrackingIdMap[n] == trackingId) { + id = n; + } + } + + if (id < 0 && !mPointerIdBits.isFull()) { + id = mPointerIdBits.markFirstUnmarkedBit(); + mPointerTrackingIdMap[id] = trackingId; + } + } + if (id < 0) { + mHavePointerIds = false; + outState->rawPointerData.clearIdBits(); + newPointerIdBits.clear(); + } else { + outPointer.id = id; + outState->rawPointerData.idToIndex[id] = outCount; + outState->rawPointerData.markIdBit(id, isHovering); + newPointerIdBits.markBit(id); + } + } + outCount += 1; + } + + outState->rawPointerData.pointerCount = outCount; + mPointerIdBits = newPointerIdBits; + + mMultiTouchMotionAccumulator.finishSync(); +} + +void MultiTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); + getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); + getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); + getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); + + if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && + mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { + size_t slotCount = mRawPointerAxes.slot.maxValue + 1; + if (slotCount > MAX_SLOTS) { + ALOGW("MultiTouch Device %s reported %zu slots but the framework " + "only supports a maximum of %zu slots at this time.", + getDeviceName().c_str(), slotCount, MAX_SLOTS); + slotCount = MAX_SLOTS; + } + mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); + } else { + mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, + false /*usingSlotsProtocol*/); + } +} + +bool MultiTouchInputMapper::hasStylus() const { + return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h new file mode 100644 index 0000000000..87841ebeec --- /dev/null +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H + +#include "TouchInputMapper.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMTPositionX; } + inline int32_t getY() const { return mAbsMTPositionY; } + inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMTOrientation; } + inline int32_t getTrackingId() const { return mAbsMTTrackingId; } + inline int32_t getPressure() const { return mAbsMTPressure; } + inline int32_t getDistance() const { return mAbsMTDistance; } + inline int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse; + bool mHaveAbsMTTouchMinor; + bool mHaveAbsMTWidthMinor; + bool mHaveAbsMTToolType; + + int32_t mAbsMTPositionX; + int32_t mAbsMTPositionY; + int32_t mAbsMTTouchMajor; + int32_t mAbsMTTouchMinor; + int32_t mAbsMTWidthMajor; + int32_t mAbsMTWidthMinor; + int32_t mAbsMTOrientation; + int32_t mAbsMTTrackingId; + int32_t mAbsMTPressure; + int32_t mAbsMTDistance; + int32_t mAbsMTToolType; + + Slot(); + void clear(); + }; + + MultiTouchMotionAccumulator(); + ~MultiTouchMotionAccumulator(); + + void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); + void reset(InputDevice* device); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlotCount; } + inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } + +private: + int32_t mCurrentSlot; + Slot* mSlots; + size_t mSlotCount; + bool mUsingSlotsProtocol; + bool mHaveStylus; + + void clearSlots(int32_t initialSlot); +}; + +class MultiTouchInputMapper : public TouchInputMapper { +public: + explicit MultiTouchInputMapper(InputDevice* device); + virtual ~MultiTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; + + // Specifies the pointer id bits that are in use, and their associated tracking id. + BitSet32 mPointerIdBits; + int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp new file mode 100644 index 0000000000..e113ccad84 --- /dev/null +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "RotaryEncoderInputMapper.h" + +#include "CursorScrollAccumulator.h" + +namespace android { + +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) + : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { + mSource = AINPUT_SOURCE_ROTARY_ENCODER; +} + +RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} + +uint32_t RotaryEncoderInputMapper::getSources() { + return mSource; +} + +void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { + float res = 0.0f; + if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { + ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); + } + if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), + mScalingFactor)) { + ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," + "default to 1.0!\n"); + mScalingFactor = 1.0f; + } + info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + res * mScalingFactor); + } +} + +void RotaryEncoderInputMapper::dump(std::string& dump) { + dump += INDENT2 "Rotary Encoder Input Mapper:\n"; + dump += StringPrintf(INDENT3 "HaveWheel: %s\n", + toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); +} + +void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + if (!changes) { + mRotaryEncoderScrollAccumulator.configure(getDevice()); + } + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + std::optional internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } else { + mOrientation = DISPLAY_ORIENTATION_0; + } + } +} + +void RotaryEncoderInputMapper::reset(nsecs_t when) { + mRotaryEncoderScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + mRotaryEncoderScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void RotaryEncoderInputMapper::sync(nsecs_t when) { + PointerCoords pointerCoords; + pointerCoords.clear(); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + bool scrolled = scroll != 0; + + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + // Moving the rotary encoder should wake the device (if specified). + uint32_t policyFlags = 0; + if (scrolled && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + + // Send motion event. + if (scrolled) { + int32_t metaState = mContext->getGlobalMetaState(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + + mRotaryEncoderScrollAccumulator.finishSync(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h new file mode 100644 index 0000000000..26488373bd --- /dev/null +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H +#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H + +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +namespace android { + +class RotaryEncoderInputMapper : public InputMapper { +public: + explicit RotaryEncoderInputMapper(InputDevice* device); + virtual ~RotaryEncoderInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + CursorScrollAccumulator mRotaryEncoderScrollAccumulator; + + int32_t mSource; + float mScalingFactor; + int32_t mOrientation; + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp new file mode 100644 index 0000000000..440d282686 --- /dev/null +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchInputMapper.h" + +namespace android { + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +SingleTouchInputMapper::~SingleTouchInputMapper() {} + +void SingleTouchInputMapper::reset(nsecs_t when) { + mSingleTouchMotionAccumulator.reset(getDevice()); + + TouchInputMapper::reset(when); +} + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mSingleTouchMotionAccumulator.process(rawEvent); +} + +void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + if (mTouchButtonAccumulator.isToolActive()) { + outState->rawPointerData.pointerCount = 1; + outState->rawPointerData.idToIndex[0] = 0; + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && + mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); + outState->rawPointerData.markIdBit(0, isHovering); + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; + outPointer.id = 0; + outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); + outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); + outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + outPointer.touchMajor = 0; + outPointer.touchMinor = 0; + outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.orientation = 0; + outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); + outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); + outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + outPointer.isHovering = isHovering; + } +} + +void SingleTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); + getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); +} + +bool SingleTouchInputMapper::hasStylus() const { + return mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h new file mode 100644 index 0000000000..d6b1455b68 --- /dev/null +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H + +#include "SingleTouchMotionAccumulator.h" +#include "TouchInputMapper.h" + +namespace android { + +class SingleTouchInputMapper : public TouchInputMapper { +public: + explicit SingleTouchInputMapper(InputDevice* device); + virtual ~SingleTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp new file mode 100644 index 0000000000..4ff941f5cf --- /dev/null +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "SwitchInputMapper.h" + +namespace android { + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) + : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} + +SwitchInputMapper::~SwitchInputMapper() {} + +uint32_t SwitchInputMapper::getSources() { + return AINPUT_SOURCE_SWITCH; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->code, rawEvent->value); + break; + + case EV_SYN: + if (rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } + } +} + +void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { + if (switchCode >= 0 && switchCode < 32) { + if (switchValue) { + mSwitchValues |= 1 << switchCode; + } else { + mSwitchValues &= ~(1 << switchCode); + } + mUpdatedSwitchMask |= 1 << switchCode; + } +} + +void SwitchInputMapper::sync(nsecs_t when) { + if (mUpdatedSwitchMask) { + uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; + NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, + mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + + mUpdatedSwitchMask = 0; + } +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + +void SwitchInputMapper::dump(std::string& dump) { + dump += INDENT2 "Switch Input Mapper:\n"; + dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h new file mode 100644 index 0000000000..dd4bb9ed65 --- /dev/null +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class SwitchInputMapper : public InputMapper { +public: + explicit SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual void dump(std::string& dump); + +private: + uint32_t mSwitchValues; + uint32_t mUpdatedSwitchMask; + + void processSwitch(int32_t switchCode, int32_t switchValue); + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h new file mode 100644 index 0000000000..efa3d6d2b2 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderContext.h" + +#include + +namespace android { + +// --- Static Definitions --- + +static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { + float temp; + switch (orientation) { + case DISPLAY_ORIENTATION_90: + temp = *deltaX; + *deltaX = *deltaY; + *deltaY = -temp; + break; + + case DISPLAY_ORIENTATION_180: + *deltaX = -*deltaX; + *deltaY = -*deltaY; + break; + + case DISPLAY_ORIENTATION_270: + temp = *deltaX; + *deltaX = -*deltaY; + *deltaY = temp; + break; + } +} + +// Returns true if the pointer should be reported as being down given the specified +// button states. This determines whether the event is reported as a touch event. +static bool isPointerDown(int32_t buttonState) { + return buttonState & + (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY); +} + +static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { + if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && + (currentButtonState & buttonState)) || + (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && + !(currentButtonState & buttonState))) { + NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); + context->getListener()->notifyKey(&args); + } +} + +static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState) { + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, + AKEYCODE_BACK); + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD); +} + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp new file mode 100644 index 0000000000..6bd0ea959c --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -0,0 +1,3883 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "TouchInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchButtonAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +namespace android { + +// --- Constants --- + +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + +// Artificial latency on synthetic events created from stylus data without corresponding touch +// data. +static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); + +// --- Static Definitions --- + +template +inline static void swap(T& a, T& b) { + T temp = a; + a = b; + b = temp; +} + +static float calculateCommonVector(float a, float b) { + if (a > 0 && b > 0) { + return a < b ? a : b; + } else if (a < 0 && b < 0) { + return a > b ? a : b; + } else { + return 0; + } +} + +inline static float distance(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); +} + +inline static int32_t signExtendNybble(int32_t value) { + return value >= 8 ? value - 16 : value; +} + +// --- RawPointerAxes --- + +RawPointerAxes::RawPointerAxes() { + clear(); +} + +void RawPointerAxes::clear() { + x.clear(); + y.clear(); + pressure.clear(); + touchMajor.clear(); + touchMinor.clear(); + toolMajor.clear(); + toolMinor.clear(); + orientation.clear(); + distance.clear(); + tiltX.clear(); + tiltY.clear(); + trackingId.clear(); + slot.clear(); +} + +// --- RawPointerData --- + +RawPointerData::RawPointerData() { + clear(); +} + +void RawPointerData::clear() { + pointerCount = 0; + clearIdBits(); +} + +void RawPointerData::copyFrom(const RawPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + + int id = pointers[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { + float x = 0, y = 0; + uint32_t count = touchingIdBits.count(); + if (count) { + for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const Pointer& pointer = pointerForId(id); + x += pointer.x; + y += pointer.y; + } + x /= count; + y /= count; + } + *outX = x; + *outY = y; +} + +// --- CookedPointerData --- + +CookedPointerData::CookedPointerData() { + clear(); +} + +void CookedPointerData::clear() { + pointerCount = 0; + hoveringIdBits.clear(); + touchingIdBits.clear(); +} + +void CookedPointerData::copyFrom(const CookedPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(other.pointerProperties[i]); + pointerCoords[i].copyFrom(other.pointerCoords[i]); + + int id = pointerProperties[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device) + : InputMapper(device), + mSource(0), + mDeviceMode(DEVICE_MODE_DISABLED), + mSurfaceWidth(-1), + mSurfaceHeight(-1), + mSurfaceLeft(0), + mSurfaceTop(0), + mPhysicalWidth(-1), + mPhysicalHeight(-1), + mPhysicalLeft(0), + mPhysicalTop(0), + mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} + +TouchInputMapper::~TouchInputMapper() {} + +uint32_t TouchInputMapper::getSources() { + return mSource; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mDeviceMode != DEVICE_MODE_DISABLED) { + info->addMotionRange(mOrientedRanges.x); + info->addMotionRange(mOrientedRanges.y); + info->addMotionRange(mOrientedRanges.pressure); + + if (mOrientedRanges.haveSize) { + info->addMotionRange(mOrientedRanges.size); + } + + if (mOrientedRanges.haveTouchSize) { + info->addMotionRange(mOrientedRanges.touchMajor); + info->addMotionRange(mOrientedRanges.touchMinor); + } + + if (mOrientedRanges.haveToolSize) { + info->addMotionRange(mOrientedRanges.toolMajor); + info->addMotionRange(mOrientedRanges.toolMinor); + } + + if (mOrientedRanges.haveOrientation) { + info->addMotionRange(mOrientedRanges.orientation); + } + + if (mOrientedRanges.haveDistance) { + info->addMotionRange(mOrientedRanges.distance); + } + + if (mOrientedRanges.haveTilt) { + info->addMotionRange(mOrientedRanges.tilt); + } + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + } + info->setButtonUnderPad(mParameters.hasButtonUnderPad); + } +} + +void TouchInputMapper::dump(std::string& dump) { + dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); + dumpParameters(dump); + dumpVirtualKeys(dump); + dumpRawPointerAxes(dump); + dumpCalibration(dump); + dumpAffineTransformation(dump); + dumpSurface(dump); + + dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); + dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); + dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); + dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); + dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); + dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); + dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); + dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); + dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); + dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); + dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); + dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); + dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); + + dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); + dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", + mLastRawState.rawPointerData.pointerCount); + for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { + const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " + "toolType=%d, isHovering=%s\n", + i, pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, + pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, + pointer.distance, pointer.toolType, toString(pointer.isHovering)); + } + + dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", + mLastCookedState.buttonState); + dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", + mLastCookedState.cookedPointerData.pointerCount); + for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { + const PointerProperties& pointerProperties = + mLastCookedState.cookedPointerData.pointerProperties[i]; + const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " + "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " + "toolMinor=%0.3f, " + "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " + "toolType=%d, isHovering=%s\n", + i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), + pointerProperties.toolType, + toString(mLastCookedState.cookedPointerData.isHovering(i))); + } + + dump += INDENT3 "Stylus Fusion:\n"; + dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", + toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", + mExternalStylusFusionTimeout); + dump += INDENT3 "External Stylus State:\n"; + dumpStylusState(dump, mExternalStylusState); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); + dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); + dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); + dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); + dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); + dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); + } +} + +const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { + switch (deviceMode) { + case DEVICE_MODE_DISABLED: + return "disabled"; + case DEVICE_MODE_DIRECT: + return "direct"; + case DEVICE_MODE_UNSCALED: + return "unscaled"; + case DEVICE_MODE_NAVIGATION: + return "navigation"; + case DEVICE_MODE_POINTER: + return "pointer"; + } + return "unknown"; +} + +void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + mConfig = *config; + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + + // Configure common accumulators. + mCursorScrollAccumulator.configure(getDevice()); + mTouchButtonAccumulator.configure(getDevice()); + + // Configure absolute axis information. + configureRawPointerAxes(); + + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { + // Update location calibration to reflect current settings + updateAffineTransformation(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + // Update pointer speed. + mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + } + + bool resetNeeded = false; + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES | + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + // Configure device sources, surface dimensions, orientation and + // scaling factors. + configureSurface(when, &resetNeeded); + } + + if (changes && resetNeeded) { + // Send reset, unless this is the first time the device has been configured, + // in which case the reader will call reset itself after all mappers are ready. + getDevice()->notifyReset(when); + } +} + +void TouchInputMapper::resolveExternalStylusPresence() { + std::vector devices; + mContext->getExternalStylusDevices(devices); + mExternalStylusConnected = !devices.empty(); + + if (!mExternalStylusConnected) { + resetExternalStylus(); + } +} + +void TouchInputMapper::configureParameters() { + // Use the pointer presentation mode for devices that do not support distinct + // multitouch. The spot-based presentation relies on being able to accurately + // locate two or more fingers on the touch pad. + mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) + ? Parameters::GESTURE_MODE_SINGLE_TOUCH + : Parameters::GESTURE_MODE_MULTI_TOUCH; + + String8 gestureModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), + gestureModeString)) { + if (gestureModeString == "single-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; + } else if (gestureModeString == "multi-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; + } else if (gestureModeString != "default") { + ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); + } + } + + if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + // The device is a touch screen. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + // The device is a pointing device like a track pad. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || + getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is a touch pad of unknown purpose. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + + mParameters.hasButtonUnderPad = + getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); + + String8 deviceTypeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { + if (deviceTypeString == "touchScreen") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (deviceTypeString == "touchPad") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "touchNavigation") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (deviceTypeString != "default") { + ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + } + } + + mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + mParameters.associatedDisplayIsExternal = false; + if (mParameters.orientationAware || + mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || + mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.hasAssociatedDisplay = true; + if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { + mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + String8 uniqueDisplayId; + getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), + uniqueDisplayId); + mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); + } + } + if (getDevice()->getAssociatedDisplayPort()) { + mParameters.hasAssociatedDisplay = true; + } + + // Initial downs on external touch devices should wake the device. + // Normally we don't do this for internal touch screens to prevent them from waking + // up in your pocket but you can enable it using the input device configuration. + mParameters.wake = getDevice()->isExternal(); + getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); +} + +void TouchInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + + switch (mParameters.gestureMode) { + case Parameters::GESTURE_MODE_SINGLE_TOUCH: + dump += INDENT4 "GestureMode: single-touch\n"; + break; + case Parameters::GESTURE_MODE_MULTI_TOUCH: + dump += INDENT4 "GestureMode: multi-touch\n"; + break; + default: + assert(false); + } + + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + dump += INDENT4 "DeviceType: touchScreen\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + dump += INDENT4 "DeviceType: touchPad\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: + dump += INDENT4 "DeviceType: touchNavigation\n"; + break; + case Parameters::DEVICE_TYPE_POINTER: + dump += INDENT4 "DeviceType: pointer\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " + "displayId='%s'\n", + toString(mParameters.hasAssociatedDisplay), + toString(mParameters.associatedDisplayIsExternal), + mParameters.uniqueDisplayId.c_str()); + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void TouchInputMapper::configureRawPointerAxes() { + mRawPointerAxes.clear(); +} + +void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { + dump += INDENT3 "Raw Touch Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); +} + +bool TouchInputMapper::hasExternalStylus() const { + return mExternalStylusConnected; +} + +/** + * Determine which DisplayViewport to use. + * 1. If display port is specified, return the matching viewport. If matching viewport not + * found, then return. + * 2. If a device has associated display, get the matching viewport by either unique id or by + * the display type (internal or external). + * 3. Otherwise, use a non-display viewport. + */ +std::optional TouchInputMapper::findViewport() { + if (mParameters.hasAssociatedDisplay) { + const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // Check if uniqueDisplayId is specified in idc file. + if (!mParameters.uniqueDisplayId.empty()) { + return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); + } + + ViewportType viewportTypeToUse; + if (mParameters.associatedDisplayIsExternal) { + viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; + } else { + viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; + } + + std::optional viewport = + mConfig.getDisplayViewportByType(viewportTypeToUse); + if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { + ALOGW("Input device %s should be associated with external display, " + "fallback to internal one for the external viewport is not found.", + getDeviceName().c_str()); + viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return viewport; + } + + // No associated display, return a non-display viewport. + DisplayViewport newViewport; + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + newViewport.setNonDisplayViewport(rawWidth, rawHeight); + return std::make_optional(newViewport); +} + +void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { + int32_t oldDeviceMode = mDeviceMode; + + resolveExternalStylusPresence(); + + // Determine device mode. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && + mConfig.pointerGesturesEnabled) { + mSource = AINPUT_SOURCE_MOUSE; + mDeviceMode = DEVICE_MODE_POINTER; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && + mParameters.hasAssociatedDisplay) { + mSource = AINPUT_SOURCE_TOUCHSCREEN; + mDeviceMode = DEVICE_MODE_DIRECT; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + if (hasExternalStylus()) { + mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mDeviceMode = DEVICE_MODE_NAVIGATION; + } else { + mSource = AINPUT_SOURCE_TOUCHPAD; + mDeviceMode = DEVICE_MODE_UNSCALED; + } + + // Ensure we have valid X and Y axes. + if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { + ALOGW("Touch device '%s' did not report support for X or Y axis! " + "The device will be inoperable.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Get associated display dimensions. + std::optional newViewport = findViewport(); + if (!newViewport) { + ALOGI("Touch device '%s' could not query the properties of its associated " + "display. The device will be inoperable until the display size " + "becomes available.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + + bool viewportChanged = mViewport != *newViewport; + if (viewportChanged) { + mViewport = *newViewport; + + if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { + // Convert rotated viewport to natural surface coordinates. + int32_t naturalLogicalWidth, naturalLogicalHeight; + int32_t naturalPhysicalWidth, naturalPhysicalHeight; + int32_t naturalPhysicalLeft, naturalPhysicalTop; + int32_t naturalDeviceWidth, naturalDeviceHeight; + switch (mViewport.orientation) { + case DISPLAY_ORIENTATION_90: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalTop = mViewport.physicalLeft; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_180: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + case DISPLAY_ORIENTATION_270: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.physicalTop; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_0: + default: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.physicalLeft; + naturalPhysicalTop = mViewport.physicalTop; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + } + + if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { + ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); + naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; + naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; + } + + mPhysicalWidth = naturalPhysicalWidth; + mPhysicalHeight = naturalPhysicalHeight; + mPhysicalLeft = naturalPhysicalLeft; + mPhysicalTop = naturalPhysicalTop; + + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + + mSurfaceOrientation = + mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; + } else { + mPhysicalWidth = rawWidth; + mPhysicalHeight = rawHeight; + mPhysicalLeft = 0; + mPhysicalTop = 0; + + mSurfaceWidth = rawWidth; + mSurfaceHeight = rawHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceOrientation = DISPLAY_ORIENTATION_0; + } + } + + // If moving between pointer modes, need to reset some state. + bool deviceModeChanged = mDeviceMode != oldDeviceMode; + if (deviceModeChanged) { + mOrientedRanges.clear(); + } + + // Create or update pointer controller if needed. + if (mDeviceMode == DEVICE_MODE_POINTER || + (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { + if (mPointerController == nullptr || viewportChanged) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + } else { + mPointerController.clear(); + } + + if (viewportChanged || deviceModeChanged) { + ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + "display id %d", + getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, + mSurfaceOrientation, mDeviceMode, mViewport.displayId); + + // Configure X and Y factors. + mXScale = float(mSurfaceWidth) / rawWidth; + mYScale = float(mSurfaceHeight) / rawHeight; + mXTranslate = -mSurfaceLeft; + mYTranslate = -mSurfaceTop; + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; + mOrientedRanges.x.source = mSource; + mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; + mOrientedRanges.y.source = mSource; + + configureVirtualKeys(); + + // Scale factor for terms that are not oriented in a particular axis. + // If the pixels are square then xScale == yScale otherwise we fake it + // by choosing an average. + mGeometricScale = avg(mXScale, mYScale); + + // Size of diagonal axis. + float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Size factors. + if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; + } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; + } else { + mSizeScale = 0.0f; + } + + mOrientedRanges.haveTouchSize = true; + mOrientedRanges.haveToolSize = true; + mOrientedRanges.haveSize = true; + + mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; + mOrientedRanges.touchMajor.source = mSource; + mOrientedRanges.touchMajor.min = 0; + mOrientedRanges.touchMajor.max = diagonalSize; + mOrientedRanges.touchMajor.flat = 0; + mOrientedRanges.touchMajor.fuzz = 0; + mOrientedRanges.touchMajor.resolution = 0; + + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; + + mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; + mOrientedRanges.toolMajor.source = mSource; + mOrientedRanges.toolMajor.min = 0; + mOrientedRanges.toolMajor.max = diagonalSize; + mOrientedRanges.toolMajor.flat = 0; + mOrientedRanges.toolMajor.fuzz = 0; + mOrientedRanges.toolMajor.resolution = 0; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; + mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; + + mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; + mOrientedRanges.size.source = mSource; + mOrientedRanges.size.min = 0; + mOrientedRanges.size.max = 1.0; + mOrientedRanges.size.flat = 0; + mOrientedRanges.size.fuzz = 0; + mOrientedRanges.size.resolution = 0; + } else { + mSizeScale = 0.0f; + } + + // Pressure factors. + mPressureScale = 0; + float pressureMax = 1.0; + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || + mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { + if (mCalibration.havePressureScale) { + mPressureScale = mCalibration.pressureScale; + pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; + } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { + mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; + } + } + + mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; + mOrientedRanges.pressure.source = mSource; + mOrientedRanges.pressure.min = 0; + mOrientedRanges.pressure.max = pressureMax; + mOrientedRanges.pressure.flat = 0; + mOrientedRanges.pressure.fuzz = 0; + mOrientedRanges.pressure.resolution = 0; + + // Tilt + mTiltXCenter = 0; + mTiltXScale = 0; + mTiltYCenter = 0; + mTiltYScale = 0; + mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; + if (mHaveTilt) { + mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); + mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); + mTiltXScale = M_PI / 180; + mTiltYScale = M_PI / 180; + + mOrientedRanges.haveTilt = true; + + mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; + mOrientedRanges.tilt.source = mSource; + mOrientedRanges.tilt.min = 0; + mOrientedRanges.tilt.max = M_PI_2; + mOrientedRanges.tilt.flat = 0; + mOrientedRanges.tilt.fuzz = 0; + mOrientedRanges.tilt.resolution = 0; + } + + // Orientation + mOrientationScale = 0; + if (mHaveTilt) { + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI; + mOrientedRanges.orientation.max = M_PI; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } else if (mCalibration.orientationCalibration != + Calibration::ORIENTATION_CALIBRATION_NONE) { + if (mCalibration.orientationCalibration == + Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { + if (mRawPointerAxes.orientation.valid) { + if (mRawPointerAxes.orientation.maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; + } else if (mRawPointerAxes.orientation.minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + } else { + mOrientationScale = 0; + } + } + } + + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } + + // Distance + mDistanceScale = 0; + if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { + if (mCalibration.haveDistanceScale) { + mDistanceScale = mCalibration.distanceScale; + } else { + mDistanceScale = 1.0f; + } + } + + mOrientedRanges.haveDistance = true; + + mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; + mOrientedRanges.distance.source = mSource; + mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; + mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; + mOrientedRanges.distance.flat = 0; + mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; + mOrientedRanges.distance.resolution = 0; + } + + // Compute oriented precision, scales and ranges. + // Note that the maximum value reported is an inclusive maximum value so it is one + // unit less than the total width or height of surface. + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + + mOrientedRanges.x.min = mYTranslate; + mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; + + mOrientedRanges.y.min = mXTranslate; + mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; + break; + + default: + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + + mOrientedRanges.x.min = mXTranslate; + mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; + + mOrientedRanges.y.min = mYTranslate; + mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; + break; + } + + // Location + updateAffineTransformation(); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + // Compute pointer gesture detection parameters. + float rawDiagonal = hypotf(rawWidth, rawHeight); + float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Scale movements such that one whole swipe of the touch pad covers a + // given area relative to the diagonal size of the display when no acceleration + // is applied. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + mPointerXMovementScale = + mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYMovementScale = mPointerXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mPointerXZoomScale = + mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYZoomScale = mPointerXZoomScale; + + // Max width between pointers to detect a swipe gesture is more than some fraction + // of the diagonal axis of the touch pad. Touches that are wider than this are + // translated into freeform gestures. + mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; + + // Abort current pointer usages because the state has changed. + abortPointerUsage(when, 0 /*policyFlags*/); + } + + // Inform the dispatcher about the changes. + *outResetNeeded = true; + bumpGeneration(); + } +} + +void TouchInputMapper::dumpSurface(std::string& dump) { + dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); + dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); + dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); + dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); + dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); + dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); + dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); +} + +void TouchInputMapper::configureVirtualKeys() { + std::vector virtualKeyDefinitions; + getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + int32_t touchScreenLeft = mRawPointerAxes.x.minValue; + int32_t touchScreenTop = mRawPointerAxes.y.minValue; + int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); + int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); + + for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { + VirtualKey virtualKey; + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + int32_t dummyKeyMetaState; + uint32_t flags; + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, + &dummyKeyMetaState, &flags)) { + ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + continue; // drop the key + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitRight = + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitTop = + (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + virtualKey.hitBottom = + (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + mVirtualKeys.push_back(virtualKey); + } +} + +void TouchInputMapper::dumpVirtualKeys(std::string& dump) { + if (!mVirtualKeys.empty()) { + dump += INDENT3 "Virtual Keys:\n"; + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " + "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", + i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, + virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } +} + +void TouchInputMapper::parseCalibration() { + const PropertyMap& in = getDevice()->getConfiguration(); + Calibration& out = mCalibration; + + // Size + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; + String8 sizeCalibrationString; + if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { + if (sizeCalibrationString == "none") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } else if (sizeCalibrationString == "geometric") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } else if (sizeCalibrationString == "diameter") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; + } else if (sizeCalibrationString == "box") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; + } else if (sizeCalibrationString == "area") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; + } else if (sizeCalibrationString != "default") { + ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); + } + } + + out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); + out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); + out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); + + // Pressure + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; + String8 pressureCalibrationString; + if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { + if (pressureCalibrationString == "none") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } else if (pressureCalibrationString == "physical") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } else if (pressureCalibrationString == "amplitude") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else if (pressureCalibrationString != "default") { + ALOGW("Invalid value for touch.pressure.calibration: '%s'", + pressureCalibrationString.string()); + } + } + + out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); + + // Orientation + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; + String8 orientationCalibrationString; + if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { + if (orientationCalibrationString == "none") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } else if (orientationCalibrationString == "interpolated") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else if (orientationCalibrationString == "vector") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; + } else if (orientationCalibrationString != "default") { + ALOGW("Invalid value for touch.orientation.calibration: '%s'", + orientationCalibrationString.string()); + } + } + + // Distance + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; + String8 distanceCalibrationString; + if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + if (distanceCalibrationString == "none") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } else if (distanceCalibrationString == "scaled") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else if (distanceCalibrationString != "default") { + ALOGW("Invalid value for touch.distance.calibration: '%s'", + distanceCalibrationString.string()); + } + } + + out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; + String8 coverageCalibrationString; + if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + if (coverageCalibrationString == "none") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } else if (coverageCalibrationString == "box") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; + } else if (coverageCalibrationString != "default") { + ALOGW("Invalid value for touch.coverage.calibration: '%s'", + coverageCalibrationString.string()); + } + } +} + +void TouchInputMapper::resolveCalibration() { + // Size + if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } + } else { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } + + // Pressure + if (mRawPointerAxes.pressure.valid) { + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } + } else { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } + + // Orientation + if (mRawPointerAxes.orientation.valid) { + if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } + } else { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } + + // Distance + if (mRawPointerAxes.distance.valid) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } + } else { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } + + // Coverage + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { + mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } +} + +void TouchInputMapper::dumpCalibration(std::string& dump) { + dump += INDENT3 "Calibration:\n"; + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NONE: + dump += INDENT4 "touch.size.calibration: none\n"; + break; + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + dump += INDENT4 "touch.size.calibration: geometric\n"; + break; + case Calibration::SIZE_CALIBRATION_DIAMETER: + dump += INDENT4 "touch.size.calibration: diameter\n"; + break; + case Calibration::SIZE_CALIBRATION_BOX: + dump += INDENT4 "touch.size.calibration: box\n"; + break; + case Calibration::SIZE_CALIBRATION_AREA: + dump += INDENT4 "touch.size.calibration: area\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveSizeScale) { + dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); + } + + if (mCalibration.haveSizeBias) { + dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); + } + + if (mCalibration.haveSizeIsSummed) { + dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", + toString(mCalibration.sizeIsSummed)); + } + + // Pressure + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_NONE: + dump += INDENT4 "touch.pressure.calibration: none\n"; + break; + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + dump += INDENT4 "touch.pressure.calibration: physical\n"; + break; + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + dump += INDENT4 "touch.pressure.calibration: amplitude\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.havePressureScale) { + dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_NONE: + dump += INDENT4 "touch.orientation.calibration: none\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + dump += INDENT4 "touch.orientation.calibration: interpolated\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: + dump += INDENT4 "touch.orientation.calibration: vector\n"; + break; + default: + ALOG_ASSERT(false); + } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_NONE: + dump += INDENT4 "touch.distance.calibration: none\n"; + break; + case Calibration::DISTANCE_CALIBRATION_SCALED: + dump += INDENT4 "touch.distance.calibration: scaled\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveDistanceScale) { + dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); + } + + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_NONE: + dump += INDENT4 "touch.coverage.calibration: none\n"; + break; + case Calibration::COVERAGE_CALIBRATION_BOX: + dump += INDENT4 "touch.coverage.calibration: box\n"; + break; + default: + ALOG_ASSERT(false); + } +} + +void TouchInputMapper::dumpAffineTransformation(std::string& dump) { + dump += INDENT3 "Affine Transformation:\n"; + + dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); + dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); + dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); + dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); + dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); + dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); +} + +void TouchInputMapper::updateAffineTransformation() { + mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), + mSurfaceOrientation); +} + +void TouchInputMapper::reset(nsecs_t when) { + mCursorButtonAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + mTouchButtonAccumulator.reset(getDevice()); + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mRawStatesPending.clear(); + mCurrentRawState.clear(); + mCurrentCookedState.clear(); + mLastRawState.clear(); + mLastCookedState.clear(); + mPointerUsage = POINTER_USAGE_NONE; + mSentHoverEnter = false; + mHavePointerIds = false; + mCurrentMotionAborted = false; + mDownTime = 0; + + mCurrentVirtualKey.down = false; + + mPointerGesture.reset(); + mPointerSimple.reset(); + resetExternalStylus(); + + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } + + InputMapper::reset(when); +} + +void TouchInputMapper::resetExternalStylus() { + mExternalStylusState.clear(); + mExternalStylusId = -1; + mExternalStylusFusionTimeout = LLONG_MAX; + mExternalStylusDataPending = false; +} + +void TouchInputMapper::clearStylusDataPendingFlags() { + mExternalStylusDataPending = false; + mExternalStylusFusionTimeout = LLONG_MAX; +} + +void TouchInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void TouchInputMapper::sync(nsecs_t when) { + const RawState* last = + mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); + + // Push a new state. + mRawStatesPending.emplace_back(); + + RawState* next = &mRawStatesPending.back(); + next->clear(); + next->when = when; + + // Sync button state. + next->buttonState = + mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); + + // Sync scroll + next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); + next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); + mCursorScrollAccumulator.finishSync(); + + // Sync touch + syncTouch(when, next); + + // Assign pointer ids. + if (!mHavePointerIds) { + assignPointerIds(last, next); + } + +#if DEBUG_RAW_EVENTS + ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " + "hovering ids 0x%08x -> 0x%08x", + last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, + last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, + last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); +#endif + + processRawTouches(false /*timeout*/); +} + +void TouchInputMapper::processRawTouches(bool timeout) { + if (mDeviceMode == DEVICE_MODE_DISABLED) { + // Drop all input if the device is disabled. + mCurrentRawState.clear(); + mRawStatesPending.clear(); + return; + } + + // Drain any pending touch states. The invariant here is that the mCurrentRawState is always + // valid and must go through the full cook and dispatch cycle. This ensures that anything + // touching the current state will only observe the events that have been dispatched to the + // rest of the pipeline. + const size_t N = mRawStatesPending.size(); + size_t count; + for (count = 0; count < N; count++) { + const RawState& next = mRawStatesPending[count]; + + // A failure to assign the stylus id means that we're waiting on stylus data + // and so should defer the rest of the pipeline. + if (assignExternalStylusId(next, timeout)) { + break; + } + + // All ready to go. + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(next); + if (mCurrentRawState.when < mLastRawState.when) { + mCurrentRawState.when = mLastRawState.when; + } + cookAndDispatch(mCurrentRawState.when); + } + if (count != 0) { + mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); + } + + if (mExternalStylusDataPending) { + if (timeout) { + nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(mLastRawState); +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, synthesizing event with new stylus data"); +#endif + cookAndDispatch(when); + } else if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::cookAndDispatch(nsecs_t when) { + // Always start with a clean state. + mCurrentCookedState.clear(); + + // Apply stylus buttons to current raw state. + applyExternalStylusButtonState(when); + + // Handle policy on initial down or hover events. + bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + mCurrentRawState.rawPointerData.pointerCount != 0; + + uint32_t policyFlags = 0; + bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; + if (initialDown || buttonsPressed) { + // If this is a touch screen, hide the pointer on an initial down. + if (mDeviceMode == DEVICE_MODE_DIRECT) { + getContext()->fadePointer(); + } + + if (mParameters.wake) { + policyFlags |= POLICY_FLAG_WAKE; + } + } + + // Consume raw off-screen touches before cooking pointer data. + // If touches are consumed, subsequent code will not receive any pointer data. + if (consumeRawTouches(when, policyFlags)) { + mCurrentRawState.rawPointerData.clear(); + } + + // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure + // with cooked pointer data that has the same ids and indices as the raw data. + // The following code can use either the raw or cooked data, as needed. + cookPointerData(); + + // Apply stylus pressure to current cooked state. + applyExternalStylusTouchState(when); + + // Synthesize key down from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Dispatch the touches either directly or by translation through a pointer on screen. + if (mDeviceMode == DEVICE_MODE_POINTER) { + for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mCurrentCookedState.fingerIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { + mCurrentCookedState.mouseIdBits.markBit(id); + } + } + for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } + } + + // Stylus takes precedence over all tools, then mouse, then finger. + PointerUsage pointerUsage = mPointerUsage; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + mCurrentCookedState.mouseIdBits.clear(); + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_STYLUS; + } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_MOUSE; + } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || + isPointerDown(mCurrentRawState.buttonState)) { + pointerUsage = POINTER_USAGE_GESTURES; + } + + dispatchPointerUsage(when, policyFlags, pointerUsage); + } else { + if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && + mPointerController != nullptr) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); + } + + if (!mCurrentMotionAborted) { + dispatchButtonRelease(when, policyFlags); + dispatchHoverExit(when, policyFlags); + dispatchTouches(when, policyFlags); + dispatchHoverEnterAndMove(when, policyFlags); + dispatchButtonPress(when, policyFlags); + } + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentMotionAborted = false; + } + } + + // Synthesize key up from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Clear some transient state. + mCurrentRawState.rawVScroll = 0; + mCurrentRawState.rawHScroll = 0; + + // Copy current touch to last touch in preparation for the next cycle. + mLastRawState.copyFrom(mCurrentRawState); + mLastCookedState.copyFrom(mCurrentCookedState); +} + +void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { + mCurrentRawState.buttonState |= mExternalStylusState.buttons; + } +} + +void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { + CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; + const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + + if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { + float pressure = mExternalStylusState.pressure; + if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { + const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); + pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + + PointerProperties& properties = + currentPointerData.editPointerPropertiesWithId(mExternalStylusId); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + properties.toolType = mExternalStylusState.toolType; + } + } +} + +bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { + if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { + return false; + } + + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + state.rawPointerData.pointerCount != 0; + if (initialDown) { + if (mExternalStylusState.pressure != 0.0f) { +#if DEBUG_STYLUS_FUSION + ALOGD("Have both stylus and touch data, beginning fusion"); +#endif + mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + } else if (timeout) { +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, assuming touch is not a stylus."); +#endif + resetExternalStylus(); + } else { + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } +#if DEBUG_STYLUS_FUSION + ALOGD("No stylus data but stylus is connected, requesting timeout " + "(%" PRId64 "ms)", + mExternalStylusFusionTimeout); +#endif + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; + } + } + + // Check if the stylus pointer has gone up. + if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { +#if DEBUG_STYLUS_FUSION + ALOGD("Stylus pointer is going up"); +#endif + mExternalStylusId = -1; + } + + return false; +} + +void TouchInputMapper::timeoutExpired(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + if (mPointerUsage == POINTER_USAGE_GESTURES) { + dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); + } + } else if (mDeviceMode == DEVICE_MODE_DIRECT) { + if (mExternalStylusFusionTimeout < when) { + processRawTouches(true /*timeout*/); + } else if (mExternalStylusFusionTimeout != LLONG_MAX) { + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::updateExternalStylusState(const StylusState& state) { + mExternalStylusState.copyFrom(state); + if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { + // We're either in the middle of a fused stream of data or we're waiting on data before + // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus + // data. + mExternalStylusDataPending = true; + processRawTouches(false /*timeout*/); + } +} + +bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { + // Check for release of a virtual key. + if (mCurrentVirtualKey.down) { + if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + return true; + } + + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return true; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation but do not consume the touch yet. + // This is useful when the user swipes through from the virtual key area + // into the main display surface. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, + mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | + AKEY_EVENT_FLAG_CANCELED); + } + } + + if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer just went down. Check for virtual key press or off-screen touches. + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); + if (!isPointInsideSurface(pointer.x, pointer.y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; + mCurrentVirtualKey.ignored = + mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, + virtualKey->scanCode); + + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + } + } + return true; + } + } + + // Disable all virtual key touches that happen within a short time interval of the + // most recent touch within the screen area. The idea is to filter out stray + // virtual key presses when interacting with the touch screen. + // + // Problems we're trying to solve: + // + // 1. While scrolling a list or dragging the window shade, the user swipes down into a + // virtual key area that is implemented by a separate touch panel and accidentally + // triggers a virtual key. + // + // 2. While typing in the on screen keyboard, the user taps slightly outside the screen + // area and accidentally triggers a virtual key. This often happens when virtual keys + // are layed out below the screen near to where the on screen keyboard's space bar + // is displayed. + if (mConfig.virtualKeyQuietTime > 0 && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); + } + return false; +} + +void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags) { + int32_t keyCode = mCurrentVirtualKey.keyCode; + int32_t scanCode = mCurrentVirtualKey.scanCode; + nsecs_t downTime = mCurrentVirtualKey.downTime; + int32_t metaState = mContext->getGlobalMetaState(); + policyFlags |= POLICY_FLAG_VIRTUAL; + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); + getListener()->notifyKey(&args); +} + +void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + if (!currentIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mCurrentMotionAborted = true; + } +} + +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + if (currentIdBits == lastIdBits) { + if (!currentIdBits.isEmpty()) { + // No pointer id changes so this is a move event. + // The listener takes care of batching moves so we don't have to deal with that here. + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } else { + // 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 moveIdBits(lastIdBits.value & currentIdBits.value); + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = + updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, moveIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + + // Dispatch pointer up events. + while (!upIdBits.isEmpty()) { + uint32_t upId = upIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); + } + + // 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 && !moveIdBits.isEmpty()) { + ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + + // Dispatch pointer down events using the new pointer locations. + while (!downIdBits.isEmpty()) { + uint32_t downId = downIdBits.clearFirstMarkedBit(); + dispatchedIdBits.markBit(downId); + + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. + mDownTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, + downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } +} + +void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { + if (mSentHoverEnter && + (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || + !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { + int32_t metaState = getContext()->getGlobalMetaState(); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastCookedState.buttonState, 0, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, + mOrientedYPrecision, mDownTime); + mSentHoverEnter = false; + } +} + +void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { + if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && + !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + if (!mSentHoverEnter) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mSentHoverEnter = true; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { + BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!releasedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); + buttonState &= ~actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + actionButton, 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { + BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!pressedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); + buttonState |= actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, + 0, metaState, buttonState, 0, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { + if (!cookedPointerData.touchingIdBits.isEmpty()) { + return cookedPointerData.touchingIdBits; + } + return cookedPointerData.hoveringIdBits; +} + +void TouchInputMapper::cookPointerData() { + uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; + + mCurrentCookedState.cookedPointerData.clear(); + mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; + mCurrentCookedState.cookedPointerData.hoveringIdBits = + mCurrentRawState.rawPointerData.hoveringIdBits; + mCurrentCookedState.cookedPointerData.touchingIdBits = + mCurrentRawState.rawPointerData.touchingIdBits; + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentCookedState.buttonState = 0; + } else { + mCurrentCookedState.buttonState = mCurrentRawState.buttonState; + } + + // Walk through the the active pointers and map device coordinates onto + // surface coordinates and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; + + // Size + float touchMajor, touchMinor, toolMajor, toolMinor, size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + case Calibration::SIZE_CALIBRATION_DIAMETER: + case Calibration::SIZE_CALIBRATION_BOX: + case Calibration::SIZE_CALIBRATION_AREA: + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { + touchMajor = in.touchMajor; + touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + toolMajor = in.toolMajor; + toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.touchMajor.valid) { + toolMajor = touchMajor = in.touchMajor; + toolMinor = touchMinor = + mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.toolMajor.valid) { + touchMajor = toolMajor = in.toolMajor; + touchMinor = toolMinor = + mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + } else { + ALOG_ASSERT(false, + "No touch or tool axes. " + "Size calibration should have been resolved to NONE."); + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + } + + if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { + uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); + if (touchingCount > 1) { + touchMajor /= touchingCount; + touchMinor /= touchingCount; + toolMajor /= touchingCount; + toolMinor /= touchingCount; + size /= touchingCount; + } + } + + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { + touchMajor *= mGeometricScale; + touchMinor *= mGeometricScale; + toolMajor *= mGeometricScale; + toolMinor *= mGeometricScale; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { + touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; + touchMinor = touchMajor; + toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; + toolMinor = toolMajor; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { + touchMinor = touchMajor; + toolMinor = toolMajor; + } + + mCalibration.applySizeScaleAndBias(&touchMajor); + mCalibration.applySizeScaleAndBias(&touchMinor); + mCalibration.applySizeScaleAndBias(&toolMajor); + mCalibration.applySizeScaleAndBias(&toolMinor); + size *= mSizeScale; + break; + default: + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + break; + } + + // Pressure + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = in.pressure * mPressureScale; + break; + default: + pressure = in.isHovering ? 0 : 1; + break; + } + + // Tilt and Orientation + float tilt; + float orientation; + if (mHaveTilt) { + float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; + float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; + orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); + tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); + } else { + tilt = 0; + + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mOrientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float confidence = hypotf(c1, c2); + float scale = 1.0f + confidence / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; + } + break; + } + default: + orientation = 0; + } + } + + // Distance + float distance; + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_SCALED: + distance = in.distance * mDistanceScale; + break; + default: + distance = 0; + } + + // Coverage + int32_t rawLeft, rawTop, rawRight, rawBottom; + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_BOX: + rawLeft = (in.toolMinor & 0xffff0000) >> 16; + rawRight = in.toolMinor & 0x0000ffff; + rawBottom = in.toolMajor & 0x0000ffff; + rawTop = (in.toolMajor & 0xffff0000) >> 16; + break; + default: + rawLeft = rawTop = rawRight = rawBottom = 0; + break; + } + + // Adjust X,Y coords for device calibration + // TODO: Adjust coverage coords? + float xTransformed = in.x, yTransformed = in.y; + mAffineTransform.applyTo(xTransformed, yTransformed); + + // Adjust X, Y, and coverage coords for surface orientation. + float x, y; + float left, top, right, bottom; + + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + orientation -= M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; + y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + orientation -= M_PI; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; + y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + orientation += M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation > mOrientedRanges.orientation.max) { + orientation -= + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + default: + x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + break; + } + + // Write output coords. + PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); + out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); + } else { + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + } + + // Write output properties. + PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; + uint32_t id = in.id; + properties.clear(); + properties.id = id; + properties.toolType = in.toolType; + + // Write id index. + mCurrentCookedState.cookedPointerData.idToIndex[id] = i; + } +} + +void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, + PointerUsage pointerUsage) { + if (pointerUsage != mPointerUsage) { + abortPointerUsage(when, policyFlags); + mPointerUsage = pointerUsage; + } + + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); + break; + case POINTER_USAGE_STYLUS: + dispatchPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + dispatchPointerMouse(when, policyFlags); + break; + default: + break; + } +} + +void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + abortPointerGestures(when, policyFlags); + break; + case POINTER_USAGE_STYLUS: + abortPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + abortPointerMouse(when, policyFlags); + break; + default: + break; + } + + mPointerUsage = POINTER_USAGE_NONE; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + bool sendEvents = + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); + if (!sendEvents) { + return; + } + if (finishPreviousGesture) { + cancelPreviousGesture = false; + } + + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + + if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); + } + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } + + // Show or hide the pointer if needed. + switch (mPointerGesture.currentGestureMode) { + case PointerGesture::NEUTRAL: + case PointerGesture::QUIET: + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { + // Remind the user of where the pointer is after finishing a gesture with spots. + mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + } + break; + case PointerGesture::TAP: + case PointerGesture::TAP_DRAG: + case PointerGesture::BUTTON_CLICK_OR_DRAG: + case PointerGesture::HOVER: + case PointerGesture::PRESS: + case PointerGesture::SWIPE: + // Unfade the pointer when the current gesture manipulates the + // area directly under the pointer. + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + break; + case PointerGesture::FREEFORM: + // Fade the pointer when the current gesture manipulates a different + // area and there are spots to guide the user experience. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } else { + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + break; + } + + // Send events! + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || + mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE || + mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture && + !mPointerGesture.lastGestureIdBits.isEmpty() && + !mPointerGesture.currentGestureIdBits.isEmpty()) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & + mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = + dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; + } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); + } + } + } + + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & + ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.clearFirstMarkedBit(); + dispatchedGestureIdBits.markBit(id); + + if (dispatchedGestureIdBits.count() == 1) { + mPointerGesture.downTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + } + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + const int32_t displayId = mPointerController->getDisplayId(); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGestureIdBits.clear(); + } else { + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureProperties[index].copyFrom( + mPointerGesture.currentGestureProperties[index]); + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Cancel previously dispatches pointers. + if (!mPointerGesture.lastGestureIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentRawState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Reset the current pointer gesture. + mPointerGesture.reset(); + mPointerVelocityControl.reset(); + + // Remove any current spots. + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } +} + +bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + // Handle TAP timeout. + if (isTimeout) { +#if DEBUG_GESTURES + ALOGD("Gestures: Processing timeout"); +#endif + + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + // The tap/drag timeout has not yet expired. + getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + + mConfig.pointerGestureTapDragInterval); + } else { + // The tap is finished. +#if DEBUG_GESTURES + ALOGD("Gestures: TAP finished"); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + return true; + } + } + + // We did not handle this timeout. + return false; + } + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + positions[count].x = pointer.x * mPointerXMovementScale; + positions[count].y = pointer.y * mPointerYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, + positions); + } + + // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning + // to NEUTRAL, then we should not generate tap event. + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && + mPointerGesture.lastGestureMode != PointerGesture::TAP && + mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { + mPointerGesture.resetTap(); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We keep the same active touch id for as long as possible. + int32_t lastActiveTouchId = mPointerGesture.activeTouchId; + int32_t activeTouchId = lastActiveTouchId; + if (activeTouchId < 0) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + mPointerGesture.firstTouchTime = when; + } + } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; + } + } + + // Determine whether we are in quiet time. + bool isQuietTime = false; + if (activeTouchId < 0) { + mPointerGesture.resetQuietTime(); + } else { + isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || + mPointerGesture.lastGestureMode == PointerGesture::SWIPE || + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && + currentFingerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && + currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + ALOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + } else if (isPointerDown(mCurrentRawState.buttonState)) { + // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " + "currentFingerCount=%d", + activeTouchId, currentFingerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0 && currentFingerCount > 1) { + int32_t bestId = -1; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = hypotf(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", + bestId, bestSpeed); +#endif + } + } + + float deltaX = 0, deltaY = 0; + if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the click will occur at the position of the anchor + // spot and all other spots will move there. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (currentFingerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } + + // Watch for taps coming out of HOVER or TAP_DRAG mode. + // Checking for taps after TAP_DRAG allows us to detect double-taps. + bool tapped = false; + if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || + mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && + lastFingerCount == 1) { + if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP"); +#endif + + mPointerGesture.tapUpTime = when; + getContext()->requestTimeoutAtTime(when + + mConfig.pointerGestureTapDragInterval); + + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = + mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.tapX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.tapY); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + tapped = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, + y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + if (mPointerGesture.tapDownTime != LLONG_MIN) { + ALOGD("Gestures: Not a TAP, %0.3fms since down", + (when - mPointerGesture.tapDownTime) * 0.000001f); + } else { + ALOGD("Gestures: Not a TAP, incompatible mode transitions"); + } +#endif + } + } + + mPointerVelocityControl.reset(); + + if (!tapped) { +#if DEBUG_GESTURES + ALOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (currentFingerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) + // The pointer follows the active touch point. + // When in HOVER, emit HOVER_MOVE events at the pointer location. + // When in TAP_DRAG, emit MOVE events at the pointer location. + ALOG_ASSERT(activeTouchId >= 0); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", + (when - mPointerGesture.tapUpTime) * 0.000001f); +#endif + } + } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } + + float deltaX = 0, deltaY = 0; + if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the hover or drag will occur at the position of the anchor spot. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + bool down; + if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP_DRAG"); +#endif + down = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: HOVER"); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } + mPointerGesture.activeGestureId = 0; + down = false; + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + down ? 1.0f : 0.0f); + + if (lastFingerCount == 0 && currentFingerCount != 0) { + mPointerGesture.resetTap(); + mPointerGesture.tapDownTime = when; + mPointerGesture.tapX = x; + mPointerGesture.tapY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) + // We need to provide feedback for each finger that goes down so we cannot wait + // for the fingers to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they + // have not moved enough to determine whether they are part of a drag or part of a + // freeform gesture, or just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a + // down at the pointer location. + // + // When the two fingers move enough or when additional fingers are added, we make + // a decision to transition into SWIPE or FREEFORM mode accordingly. + ALOG_ASSERT(activeTouchId >= 0); + + bool settled = when >= + mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; + if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && + mPointerGesture.lastGestureMode != PointerGesture::SWIPE && + mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + *outFinishPreviousGesture = true; + } else if (!settled && currentFingerCount > lastFingerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + *outCancelPreviousGesture = true; + } else { + // Continue previous gesture. + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (*outFinishPreviousGesture || *outCancelPreviousGesture) { + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; + mPointerGesture.referenceIdBits.clear(); + mPointerVelocityControl.reset(); + + // Use the centroid and pointer location as the reference points for the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + mCurrentRawState.rawPointerData + .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & + ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); + const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; + } else { + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); + } + } + + // Consider transitions from PRESS to SWIPE or MULTITOUCH. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + if (currentFingerCount > 2) { + // There are more than two pointers, switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are exactly two pointers. + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + uint32_t id1 = idBits.clearFirstMarkedBit(); + uint32_t id2 = idBits.firstMarkedBit(); + const RawPointerData::Pointer& p1 = + mCurrentRawState.rawPointerData.pointerForId(id1); + const RawPointerData::Pointer& p2 = + mCurrentRawState.rawPointerData.pointerForId(id2); + float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); + if (mutualDistance > mPointerGestureMaxSwipeWidth) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + mutualDistance, mPointerGestureMaxSwipeWidth); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && + dist2 >= mConfig.pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * + // mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dx1 = delta1.dx * mPointerXZoomScale; + float dy1 = delta1.dy * mPointerYZoomScale; + float dx2 = delta2.dx * mPointerXZoomScale; + float dy2 = delta2.dy * mPointerYZoomScale; + float dot = dx1 * dx2 + dy1 * dy2; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + } + } + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (currentFingerCount > 2) { +#if DEBUG_GESTURES + ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && + (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx = 0; + delta.dy = 0; + } + + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; + + commonDeltaX *= mPointerXMovementScale; + commonDeltaY *= mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); + mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; + } + + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // PRESS or SWIPE mode. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & + ~mCurrentCookedState.fingerIdBits.value); + !upTouchIdBits.isEmpty();) { + uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } + +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif + + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + for (uint32_t i = 0; i < currentFingerCount; i++) { + uint32_t touchId = idBits.clearFirstMarkedBit(); + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.markFirstUnmarkedBit(); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(touchId); + float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; + float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + + mPointerGesture.currentGestureProperties[i].clear(); + mPointerGesture.currentGestureProperties[i].id = gestureId; + mPointerGesture.currentGestureProperties[i].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX + deltaX); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY + deltaY); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + 1.0f); + } + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM new " + "activeGestureId=%d", + mPointerGesture.activeGestureId); +#endif + } + } + } + + mPointerController->setButtonState(mCurrentRawState.buttonState); + +#if DEBUG_GESTURES + ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif + return true; +} + +void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); + uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; + float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); + float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); + mPointerController->setPosition(x, y); + + hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); + down = !hovering; + + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[index]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; + } else { + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); +} + +void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); + uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + float deltaX = 0, deltaY = 0; + if (mLastCookedState.mouseIdBits.hasBit(id)) { + uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - + mLastRawState.rawPointerData.pointers[lastIndex].x) * + mPointerXMovementScale; + deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - + mLastRawState.rawPointerData.pointers[lastIndex].y) * + mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + down = isPointerDown(mCurrentRawState.buttonState); + hovering = !down; + + float x, y; + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + hovering ? 0.0f : 1.0f); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; + } else { + mPointerVelocityControl.reset(); + + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); + + mPointerVelocityControl.reset(); +} + +void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, + bool hovering) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t displayId = mViewport.displayId; + + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + + if (mPointerSimple.down && !down) { + mPointerSimple.down = false; + + // Send up. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mPointerSimple.hovering && !hovering) { + mPointerSimple.hovering = false; + + // Send hover exit. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (down) { + if (!mPointerSimple.down) { + mPointerSimple.down = true; + mPointerSimple.downTime = when; + + // Send down. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (hovering) { + if (!mPointerSimple.hovering) { + mPointerSimple.hovering = true; + + // Send hover enter. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send hover move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { + float vscroll = mCurrentRawState.rawVScroll; + float hscroll = mCurrentRawState.rawHScroll; + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + // Send scroll. + PointerCoords pointerCoords; + pointerCoords.copyFrom(mPointerSimple.currentCoords); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Save state. + if (down || hovering) { + mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); + mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); + } else { + mPointerSimple.reset(); + } +} + +void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + dispatchPointerSimple(when, policyFlags, false, false); +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, + const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, + BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + PointerProperties pointerProperties[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = idToIndex[id]; + pointerProperties[pointerCount].copyFrom(properties[index]); + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + ALOG_ASSERT(pointerCount != 0); + + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + ALOG_ASSERT(false); + } + } + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } + const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); + const int32_t deviceId = getDeviceId(); + std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::for_each(frames.begin(), frames.end(), + [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); + getListener()->notifyMotion(&args); +} + +bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, + const PointerCoords* inCoords, + const uint32_t* inIdToIndex, + PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +void TouchInputMapper::cancelTouch(nsecs_t when) { + abortPointerUsage(when, 0 /*policyFlags*/); + abortTouches(when, 0 /* policyFlags*/); +} + +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + const float scaledX = x * mXScale; + const float scaledY = y * mYScale; + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && + scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && + scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; +} + +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { + for (const VirtualKey& virtualKey : mVirtualKeys) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return &virtualKey; + } + } + + return nullptr; +} + +void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { + uint32_t currentPointerCount = current->rawPointerData.pointerCount; + uint32_t lastPointerCount = last->rawPointerData.pointerCount; + + current->rawPointerData.clearIdBits(); + + if (currentPointerCount == 0) { + // No pointers to assign. + return; + } + + if (lastPointerCount == 0) { + // All pointers are new. + for (uint32_t i = 0; i < currentPointerCount; i++) { + uint32_t id = i; + current->rawPointerData.pointers[i].id = id; + current->rawPointerData.idToIndex[id] = i; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); + } + return; + } + + if (currentPointerCount == 1 && lastPointerCount == 1 && + current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = last->rawPointerData.pointers[0].id; + current->rawPointerData.pointers[0].id = id; + current->rawPointerData.idToIndex[id] = 0; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); + return; + } + + // General case. + // 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. + // The pointers must have the same tool type but it is possible for them to + // transition from hovering to touching or vice-versa while retaining the same id. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + const RawPointerData::Pointer& currentPointer = + current->rawPointerData.pointers[currentPointerIndex]; + const RawPointerData::Pointer& lastPointer = + last->rawPointerData.pointers[lastPointerIndex]; + if (currentPointer.toolType == lastPointer.toolType) { + int64_t deltaX = currentPointer.x - lastPointer.x; + int64_t deltaY = currentPointer.y - lastPointer.y; + + 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; + } + } + } + + // 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; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, 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. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { + while (heapSize > 0) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0;;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize && + heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, + heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + } +#endif + } + + heapSize -= 1; + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering( + currentPointerIndex)); + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 + ", distance=%" PRIu64, + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to pointers that were not matched in the process. + for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { + uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); + uint32_t id = usedIdBits.markFirstUnmarkedBit(); + + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering(currentPointerIndex)); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); +#endif + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + for (const VirtualKey& virtualKey : mVirtualKeys) { + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + + return true; +} + +std::optional TouchInputMapper::getAssociatedDisplayId() { + if (mParameters.hasAssociatedDisplay) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + return std::make_optional(mViewport.displayId); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h new file mode 100644 index 0000000000..cf6cd76b87 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "EventHub.h" +#include "InputMapper.h" +#include "InputReaderBase.h" +#include "TouchButtonAccumulator.h" + +#include + +namespace android { + +/* Raw axis information from the driver. */ +struct RawPointerAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + RawAbsoluteAxisInfo distance; + RawAbsoluteAxisInfo tiltX; + RawAbsoluteAxisInfo tiltY; + RawAbsoluteAxisInfo trackingId; + RawAbsoluteAxisInfo slot; + + RawPointerAxes(); + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } + inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } + void clear(); +}; + +/* Raw data for a collection of pointers including a pointer id mapping table. */ +struct RawPointerData { + struct Pointer { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + int32_t distance; + int32_t tiltX; + int32_t tiltY; + int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant + bool isHovering; + }; + + uint32_t pointerCount; + Pointer pointers[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + RawPointerData(); + void clear(); + void copyFrom(const RawPointerData& other); + void getCentroidOfTouchingPointers(float* outX, float* outY) const; + + inline void markIdBit(uint32_t id, bool isHovering) { + if (isHovering) { + hoveringIdBits.markBit(id); + } else { + touchingIdBits.markBit(id); + } + } + + inline void clearIdBits() { + hoveringIdBits.clear(); + touchingIdBits.clear(); + } + + inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } + + inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } +}; + +/* Cooked data for a collection of pointers including a pointer id mapping table. */ +struct CookedPointerData { + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + CookedPointerData(); + void clear(); + void copyFrom(const CookedPointerData& other); + + inline const PointerCoords& pointerCoordsForId(uint32_t id) const { + return pointerCoords[idToIndex[id]]; + } + + inline PointerCoords& editPointerCoordsWithId(uint32_t id) { + return pointerCoords[idToIndex[id]]; + } + + inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { + return pointerProperties[idToIndex[id]]; + } + + inline bool isHovering(uint32_t pointerIndex) const { + return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); + } + + inline bool isTouching(uint32_t pointerIndex) const { + return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); + } +}; + +class TouchInputMapper : public InputMapper { +public: + explicit TouchInputMapper(InputDevice* device); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void fadePointer(); + virtual void cancelTouch(nsecs_t when); + virtual void timeoutExpired(nsecs_t when); + virtual void updateExternalStylusState(const StylusState& state); + virtual std::optional getAssociatedDisplayId(); + +protected: + CursorButtonAccumulator mCursorButtonAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + // Input sources and device mode. + uint32_t mSource; + + enum DeviceMode { + DEVICE_MODE_DISABLED, // input is disabled + DEVICE_MODE_DIRECT, // direct mapping (touchscreen) + DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) + DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) + DEVICE_MODE_POINTER, // pointer mapping (pointer) + }; + DeviceMode mDeviceMode; + + // The reader's configuration. + InputReaderConfiguration mConfig; + + // Immutable configuration parameters. + struct Parameters { + enum DeviceType { + DEVICE_TYPE_TOUCH_SCREEN, + DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_TOUCH_NAVIGATION, + DEVICE_TYPE_POINTER, + }; + + DeviceType deviceType; + bool hasAssociatedDisplay; + bool associatedDisplayIsExternal; + bool orientationAware; + bool hasButtonUnderPad; + std::string uniqueDisplayId; + + enum GestureMode { + GESTURE_MODE_SINGLE_TOUCH, + GESTURE_MODE_MULTI_TOUCH, + }; + GestureMode gestureMode; + + bool wake; + } mParameters; + + // Immutable calibration parameters in parsed form. + struct Calibration { + // Size + enum SizeCalibration { + SIZE_CALIBRATION_DEFAULT, + SIZE_CALIBRATION_NONE, + SIZE_CALIBRATION_GEOMETRIC, + SIZE_CALIBRATION_DIAMETER, + SIZE_CALIBRATION_BOX, + SIZE_CALIBRATION_AREA, + }; + + SizeCalibration sizeCalibration; + + bool haveSizeScale; + float sizeScale; + bool haveSizeBias; + float sizeBias; + bool haveSizeIsSummed; + bool sizeIsSummed; + + // Pressure + enum PressureCalibration { + PRESSURE_CALIBRATION_DEFAULT, + PRESSURE_CALIBRATION_NONE, + PRESSURE_CALIBRATION_PHYSICAL, + PRESSURE_CALIBRATION_AMPLITUDE, + }; + + PressureCalibration pressureCalibration; + bool havePressureScale; + float pressureScale; + + // Orientation + enum OrientationCalibration { + ORIENTATION_CALIBRATION_DEFAULT, + ORIENTATION_CALIBRATION_NONE, + ORIENTATION_CALIBRATION_INTERPOLATED, + ORIENTATION_CALIBRATION_VECTOR, + }; + + OrientationCalibration orientationCalibration; + + // Distance + enum DistanceCalibration { + DISTANCE_CALIBRATION_DEFAULT, + DISTANCE_CALIBRATION_NONE, + DISTANCE_CALIBRATION_SCALED, + }; + + DistanceCalibration distanceCalibration; + bool haveDistanceScale; + float distanceScale; + + enum CoverageCalibration { + COVERAGE_CALIBRATION_DEFAULT, + COVERAGE_CALIBRATION_NONE, + COVERAGE_CALIBRATION_BOX, + }; + + CoverageCalibration coverageCalibration; + + inline void applySizeScaleAndBias(float* outSize) const { + if (haveSizeScale) { + *outSize *= sizeScale; + } + if (haveSizeBias) { + *outSize += sizeBias; + } + if (*outSize < 0) { + *outSize = 0; + } + } + } mCalibration; + + // Affine location transformation/calibration + struct TouchAffineTransformation mAffineTransform; + + RawPointerAxes mRawPointerAxes; + + struct RawState { + nsecs_t when; + + // Raw pointer sample data. + RawPointerData rawPointerData; + + int32_t buttonState; + + // Scroll state. + int32_t rawVScroll; + int32_t rawHScroll; + + void copyFrom(const RawState& other) { + when = other.when; + rawPointerData.copyFrom(other.rawPointerData); + buttonState = other.buttonState; + rawVScroll = other.rawVScroll; + rawHScroll = other.rawHScroll; + } + + void clear() { + when = 0; + rawPointerData.clear(); + buttonState = 0; + rawVScroll = 0; + rawHScroll = 0; + } + }; + + struct CookedState { + // Cooked pointer sample data. + CookedPointerData cookedPointerData; + + // Id bits used to differentiate fingers, stylus and mouse tools. + BitSet32 fingerIdBits; + BitSet32 stylusIdBits; + BitSet32 mouseIdBits; + + int32_t buttonState; + + void copyFrom(const CookedState& other) { + cookedPointerData.copyFrom(other.cookedPointerData); + fingerIdBits = other.fingerIdBits; + stylusIdBits = other.stylusIdBits; + mouseIdBits = other.mouseIdBits; + buttonState = other.buttonState; + } + + void clear() { + cookedPointerData.clear(); + fingerIdBits.clear(); + stylusIdBits.clear(); + mouseIdBits.clear(); + buttonState = 0; + } + }; + + std::vector mRawStatesPending; + RawState mCurrentRawState; + CookedState mCurrentCookedState; + RawState mLastRawState; + CookedState mLastCookedState; + + // State provided by an external stylus + StylusState mExternalStylusState; + int64_t mExternalStylusId; + nsecs_t mExternalStylusFusionTimeout; + bool mExternalStylusDataPending; + + // True if we sent a HOVER_ENTER event. + bool mSentHoverEnter; + + // Have we assigned pointer IDs for this stream + bool mHavePointerIds; + + // Is the current stream of direct touch events aborted + bool mCurrentMotionAborted; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + // The pointer controller, or null if the device is not a pointer. + sp mPointerController; + + std::vector mVirtualKeys; + + virtual void configureParameters(); + virtual void dumpParameters(std::string& dump); + virtual void configureRawPointerAxes(); + virtual void dumpRawPointerAxes(std::string& dump); + virtual void configureSurface(nsecs_t when, bool* outResetNeeded); + virtual void dumpSurface(std::string& dump); + virtual void configureVirtualKeys(); + virtual void dumpVirtualKeys(std::string& dump); + virtual void parseCalibration(); + virtual void resolveCalibration(); + virtual void dumpCalibration(std::string& dump); + virtual void updateAffineTransformation(); + virtual void dumpAffineTransformation(std::string& dump); + virtual void resolveExternalStylusPresence(); + virtual bool hasStylus() const = 0; + virtual bool hasExternalStylus() const; + + virtual void syncTouch(nsecs_t when, RawState* outState) = 0; + +private: + // The current viewport. + // The components of the viewport are specified in the display's rotated orientation. + DisplayViewport mViewport; + + // The surface orientation, width and height set by configureSurface(). + // The width and height are derived from the viewport but are specified + // in the natural orientation. + // The surface origin specifies how the surface coordinates should be translated + // to align with the logical display coordinate space. + int32_t mSurfaceWidth; + int32_t mSurfaceHeight; + int32_t mSurfaceLeft; + int32_t mSurfaceTop; + + // Similar to the surface coordinates, but in the raw display coordinate space rather than in + // the logical coordinate space. + int32_t mPhysicalWidth; + int32_t mPhysicalHeight; + int32_t mPhysicalLeft; + int32_t mPhysicalTop; + + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. + int32_t mSurfaceOrientation; + + // Translation and scaling factors, orientation-independent. + float mXTranslate; + float mXScale; + float mXPrecision; + + float mYTranslate; + float mYScale; + float mYPrecision; + + float mGeometricScale; + + float mPressureScale; + + float mSizeScale; + + float mOrientationScale; + + float mDistanceScale; + + bool mHaveTilt; + float mTiltXCenter; + float mTiltXScale; + float mTiltYCenter; + float mTiltYScale; + + bool mExternalStylusConnected; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + + bool haveSize; + InputDeviceInfo::MotionRange size; + + bool haveTouchSize; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + + bool haveToolSize; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + + bool haveOrientation; + InputDeviceInfo::MotionRange orientation; + + bool haveDistance; + InputDeviceInfo::MotionRange distance; + + bool haveTilt; + InputDeviceInfo::MotionRange tilt; + + OrientedRanges() { clear(); } + + void clear() { + haveSize = false; + haveTouchSize = false; + haveToolSize = false; + haveOrientation = false; + haveDistance = false; + haveTilt = false; + } + } mOrientedRanges; + + // Oriented dimensions and precision. + float mOrientedXPrecision; + float mOrientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + bool ignored; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } mCurrentVirtualKey; + + // Scale factor for gesture or mouse based pointer movements. + float mPointerXMovementScale; + float mPointerYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float mPointerXZoomScale; + float mPointerYZoomScale; + + // The maximum swipe width. + float mPointerGestureMaxSwipeWidth; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + enum PointerUsage { + POINTER_USAGE_NONE, + POINTER_USAGE_GESTURES, + POINTER_USAGE_STYLUS, + POINTER_USAGE_MOUSE, + }; + PointerUsage mPointerUsage; + + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Exactly one finger dragging following a tap. + // Pointer follows the active finger. + // Emits DOWN, MOVE and UP events at the pointer location. + // + // Detect double-taps when the finger goes up while in TAP_DRAG mode. + TAP_DRAG, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + BUTTON_CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + // + // Detect taps when the finger goes up while in HOVER mode. + HOVER, + + // Exactly two fingers but neither have moved enough to clearly indicate + // whether a swipe or freeform gesture was intended. We consider the + // pointer to be pressed so this enables clicking or long-pressing on buttons. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. + PRESS, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // Time the first finger went down. + nsecs_t firstTouchTime; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties currentGestureProperties[MAX_POINTERS]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties lastGestureProperties[MAX_POINTERS]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time when the pointer went down for a TAP. + nsecs_t tapDownTime; + + // Time when the pointer went up for a TAP. + nsecs_t tapUpTime; + + // Location of initial tap. + float tapX, tapY; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // Reference points for multitouch gestures. + float referenceTouchX; // reference touch X/Y coordinates in surface units + float referenceTouchY; + float referenceGestureX; // reference gesture X/Y coordinates in pixels + float referenceGestureY; + + // Distance that each pointer has traveled which has not yet been + // subsumed into the reference gesture position. + BitSet32 referenceIdBits; + struct Delta { + float dx, dy; + }; + Delta referenceDeltas[MAX_POINTER_ID + 1]; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + firstTouchTime = LLONG_MIN; + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGestureIdBits.clear(); + downTime = 0; + velocityTracker.clear(); + resetTap(); + resetQuietTime(); + } + + void resetTap() { + tapDownTime = LLONG_MIN; + tapUpTime = LLONG_MIN; + } + + void resetQuietTime() { quietTime = LLONG_MIN; } + } mPointerGesture; + + struct PointerSimple { + PointerCoords currentCoords; + PointerProperties currentProperties; + PointerCoords lastCoords; + PointerProperties lastProperties; + + // True if the pointer is down. + bool down; + + // True if the pointer is hovering. + bool hovering; + + // Time the pointer last went down. + nsecs_t downTime; + + void reset() { + currentCoords.clear(); + currentProperties.clear(); + lastCoords.clear(); + lastProperties.clear(); + down = false; + hovering = false; + downTime = 0; + } + } mPointerSimple; + + // The pointer and scroll velocity controls. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + std::optional findViewport(); + + void resetExternalStylus(); + void clearStylusDataPendingFlags(); + + void sync(nsecs_t when); + + bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); + void processRawTouches(bool timeout); + void cookAndDispatch(nsecs_t when); + void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags); + + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); + void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); + void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); + void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); + const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); + void cookPointerData(); + void abortTouches(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); + void abortPointerUsage(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); + void abortPointerGestures(nsecs_t when, uint32_t policyFlags); + bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout); + + void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); + void abortPointerStylus(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); + void abortPointerMouse(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); + void abortPointerSimple(nsecs_t when, uint32_t policyFlags); + + bool assignExternalStylusId(const RawState& state, bool timeout); + void applyExternalStylusButtonState(nsecs_t when); + void applyExternalStylusTouchState(nsecs_t when); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords and properties for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, + const uint32_t* inIdToIndex, PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const; + + bool isPointInsideSurface(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); + + static void assignPointerIds(const RawState* last, RawState* current); + + const char* modeToString(DeviceMode deviceMode); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp new file mode 100644 index 0000000000..a27fab4581 --- /dev/null +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "VibratorInputMapper.h" + +namespace android { + +VibratorInputMapper::VibratorInputMapper(InputDevice* device) + : InputMapper(device), mVibrating(false) {} + +VibratorInputMapper::~VibratorInputMapper() {} + +uint32_t VibratorInputMapper::getSources() { + return 0; +} + +void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setVibrator(true); +} + +void VibratorInputMapper::process(const RawEvent* rawEvent) { + // TODO: Handle FF_STATUS, although it does not seem to be widely supported. +} + +void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +#if DEBUG_VIBRATOR + std::string patternStr; + for (size_t i = 0; i < patternSize; i++) { + if (i != 0) { + patternStr += ", "; + } + patternStr += StringPrintf("%" PRId64, pattern[i]); + } + ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), + patternStr.c_str(), repeat, token); +#endif + + mVibrating = true; + memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); + mPatternSize = patternSize; + mRepeat = repeat; + mToken = token; + mIndex = -1; + + nextStep(); +} + +void VibratorInputMapper::cancelVibrate(int32_t token) { +#if DEBUG_VIBRATOR + ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); +#endif + + if (mVibrating && mToken == token) { + stopVibrating(); + } +} + +void VibratorInputMapper::timeoutExpired(nsecs_t when) { + if (mVibrating) { + if (when >= mNextStepTime) { + nextStep(); + } else { + getContext()->requestTimeoutAtTime(mNextStepTime); + } + } +} + +void VibratorInputMapper::nextStep() { + mIndex += 1; + if (size_t(mIndex) >= mPatternSize) { + if (mRepeat < 0) { + // We are done. + stopVibrating(); + return; + } + mIndex = mRepeat; + } + + bool vibratorOn = mIndex & 1; + nsecs_t duration = mPattern[mIndex]; + if (vibratorOn) { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); +#endif + getEventHub()->vibrate(getDeviceId(), duration); + } else { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); + } + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mNextStepTime = now + duration; + getContext()->requestTimeoutAtTime(mNextStepTime); +#if DEBUG_VIBRATOR + ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); +#endif +} + +void VibratorInputMapper::stopVibrating() { + mVibrating = false; +#if DEBUG_VIBRATOR + ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); +} + +void VibratorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Vibrator Input Mapper:\n"; + dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h new file mode 100644 index 0000000000..6b33f4811e --- /dev/null +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class VibratorInputMapper : public InputMapper { +public: + explicit VibratorInputMapper(InputDevice* device); + virtual ~VibratorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void process(const RawEvent* rawEvent); + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void timeoutExpired(nsecs_t when); + virtual void dump(std::string& dump); + +private: + bool mVibrating; + nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; + size_t mPatternSize; + ssize_t mRepeat; + int32_t mToken; + ssize_t mIndex; + nsecs_t mNextStepTime; + + void nextStep(); + void stopVibrating(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp new file mode 100644 index 0000000000..0337d51126 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorButtonAccumulator::CursorButtonAccumulator() { + clearButtons(); +} + +void CursorButtonAccumulator::reset(InputDevice* device) { + mBtnLeft = device->isKeyPressed(BTN_LEFT); + mBtnRight = device->isKeyPressed(BTN_RIGHT); + mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); + mBtnBack = device->isKeyPressed(BTN_BACK); + mBtnSide = device->isKeyPressed(BTN_SIDE); + mBtnForward = device->isKeyPressed(BTN_FORWARD); + mBtnExtra = device->isKeyPressed(BTN_EXTRA); + mBtnTask = device->isKeyPressed(BTN_TASK); +} + +void CursorButtonAccumulator::clearButtons() { + mBtnLeft = 0; + mBtnRight = 0; + mBtnMiddle = 0; + mBtnBack = 0; + mBtnSide = 0; + mBtnForward = 0; + mBtnExtra = 0; + mBtnTask = 0; +} + +void CursorButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_LEFT: + mBtnLeft = rawEvent->value; + break; + case BTN_RIGHT: + mBtnRight = rawEvent->value; + break; + case BTN_MIDDLE: + mBtnMiddle = rawEvent->value; + break; + case BTN_BACK: + mBtnBack = rawEvent->value; + break; + case BTN_SIDE: + mBtnSide = rawEvent->value; + break; + case BTN_FORWARD: + mBtnForward = rawEvent->value; + break; + case BTN_EXTRA: + mBtnExtra = rawEvent->value; + break; + case BTN_TASK: + mBtnTask = rawEvent->value; + break; + } + } +} + +uint32_t CursorButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnLeft) { + result |= AMOTION_EVENT_BUTTON_PRIMARY; + } + if (mBtnRight) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnMiddle) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + if (mBtnBack || mBtnSide) { + result |= AMOTION_EVENT_BUTTON_BACK; + } + if (mBtnForward || mBtnExtra) { + result |= AMOTION_EVENT_BUTTON_FORWARD; + } + return result; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h new file mode 100644 index 0000000000..d9123109a3 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of mouse or touch pad buttons. */ +class CursorButtonAccumulator { +public: + CursorButtonAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + +private: + bool mBtnLeft; + bool mBtnRight; + bool mBtnMiddle; + bool mBtnBack; + bool mBtnSide; + bool mBtnForward; + bool mBtnExtra; + bool mBtnTask; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp new file mode 100644 index 0000000000..d744096d94 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorScrollAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::configure(InputDevice* device) { + mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); + mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +} + +void CursorScrollAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::clearRelativeAxes() { + mRelWheel = 0; + mRelHWheel = 0; +} + +void CursorScrollAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_WHEEL: + mRelWheel = rawEvent->value; + break; + case REL_HWHEEL: + mRelHWheel = rawEvent->value; + break; + } + } +} + +void CursorScrollAccumulator::finishSync() { + clearRelativeAxes(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h new file mode 100644 index 0000000000..85f331fd8a --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of cursor scrolling motions. */ + +class CursorScrollAccumulator { +public: + CursorScrollAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline bool haveRelativeVWheel() const { return mHaveRelWheel; } + inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + inline int32_t getRelativeVWheel() const { return mRelWheel; } + inline int32_t getRelativeHWheel() const { return mRelHWheel; } + +private: + bool mHaveRelWheel; + bool mHaveRelHWheel; + + int32_t mRelX; + int32_t mRelY; + int32_t mRelWheel; + int32_t mRelHWheel; + + void clearRelativeAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..e9ba727a0d --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchMotionAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { + clearAbsoluteAxes(); +} + +void SingleTouchMotionAccumulator::reset(InputDevice* device) { + mAbsX = device->getAbsoluteAxisValue(ABS_X); + mAbsY = device->getAbsoluteAxisValue(ABS_Y); + mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); + mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); + mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); + mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); + mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); +} + +void SingleTouchMotionAccumulator::clearAbsoluteAxes() { + mAbsX = 0; + mAbsY = 0; + mAbsPressure = 0; + mAbsToolWidth = 0; + mAbsDistance = 0; + mAbsTiltX = 0; + mAbsTiltY = 0; +} + +void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + switch (rawEvent->code) { + case ABS_X: + mAbsX = rawEvent->value; + break; + case ABS_Y: + mAbsY = rawEvent->value; + break; + case ABS_PRESSURE: + mAbsPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAbsToolWidth = rawEvent->value; + break; + case ABS_DISTANCE: + mAbsDistance = rawEvent->value; + break; + case ABS_TILT_X: + mAbsTiltX = rawEvent->value; + break; + case ABS_TILT_Y: + mAbsTiltY = rawEvent->value; + break; + } + } +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h new file mode 100644 index 0000000000..75f8a961b3 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H +#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of single-touch protocol. */ +class SingleTouchMotionAccumulator { +public: + SingleTouchMotionAccumulator(); + + void process(const RawEvent* rawEvent); + void reset(InputDevice* device); + + inline int32_t getAbsoluteX() const { return mAbsX; } + inline int32_t getAbsoluteY() const { return mAbsY; } + inline int32_t getAbsolutePressure() const { return mAbsPressure; } + inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } + inline int32_t getAbsoluteDistance() const { return mAbsDistance; } + inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } + inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } + +private: + int32_t mAbsX; + int32_t mAbsY; + int32_t mAbsPressure; + int32_t mAbsToolWidth; + int32_t mAbsDistance; + int32_t mAbsTiltX; + int32_t mAbsTiltY; + + void clearAbsoluteAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp new file mode 100644 index 0000000000..d2f06c86fd --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TouchButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { + clearButtons(); +} + +void TouchButtonAccumulator::configure(InputDevice* device) { + mHaveBtnTouch = device->hasKey(BTN_TOUCH); + mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || + device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || + device->hasKey(BTN_TOOL_AIRBRUSH); +} + +void TouchButtonAccumulator::reset(InputDevice* device) { + mBtnTouch = device->isKeyPressed(BTN_TOUCH); + mBtnStylus = device->isKeyPressed(BTN_STYLUS); + // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); + mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); +} + +void TouchButtonAccumulator::clearButtons() { + mBtnTouch = 0; + mBtnStylus = 0; + mBtnStylus2 = 0; + mBtnToolFinger = 0; + mBtnToolPen = 0; + mBtnToolRubber = 0; + mBtnToolBrush = 0; + mBtnToolPencil = 0; + mBtnToolAirbrush = 0; + mBtnToolMouse = 0; + mBtnToolLens = 0; + mBtnToolDoubleTap = 0; + mBtnToolTripleTap = 0; + mBtnToolQuadTap = 0; +} + +void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_TOUCH: + mBtnTouch = rawEvent->value; + break; + case BTN_STYLUS: + mBtnStylus = rawEvent->value; + break; + case BTN_STYLUS2: + case BTN_0: // BTN_0 is what gets mapped for the HID usage + // Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = rawEvent->value; + break; + case BTN_TOOL_FINGER: + mBtnToolFinger = rawEvent->value; + break; + case BTN_TOOL_PEN: + mBtnToolPen = rawEvent->value; + break; + case BTN_TOOL_RUBBER: + mBtnToolRubber = rawEvent->value; + break; + case BTN_TOOL_BRUSH: + mBtnToolBrush = rawEvent->value; + break; + case BTN_TOOL_PENCIL: + mBtnToolPencil = rawEvent->value; + break; + case BTN_TOOL_AIRBRUSH: + mBtnToolAirbrush = rawEvent->value; + break; + case BTN_TOOL_MOUSE: + mBtnToolMouse = rawEvent->value; + break; + case BTN_TOOL_LENS: + mBtnToolLens = rawEvent->value; + break; + case BTN_TOOL_DOUBLETAP: + mBtnToolDoubleTap = rawEvent->value; + break; + case BTN_TOOL_TRIPLETAP: + mBtnToolTripleTap = rawEvent->value; + break; + case BTN_TOOL_QUADTAP: + mBtnToolQuadTap = rawEvent->value; + break; + } + } +} + +uint32_t TouchButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnStylus) { + result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + } + if (mBtnStylus2) { + result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } + return result; +} + +int32_t TouchButtonAccumulator::getToolType() const { + if (mBtnToolMouse || mBtnToolLens) { + return AMOTION_EVENT_TOOL_TYPE_MOUSE; + } + if (mBtnToolRubber) { + return AMOTION_EVENT_TOOL_TYPE_ERASER; + } + if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { + return AMOTION_EVENT_TOOL_TYPE_FINGER; + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +bool TouchButtonAccumulator::isToolActive() const { + return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || + mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || + mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; +} + +bool TouchButtonAccumulator::isHovering() const { + return mHaveBtnTouch && !mBtnTouch; +} + +bool TouchButtonAccumulator::hasStylus() const { + return mHaveStylus; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h new file mode 100644 index 0000000000..65b6bdcc12 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H + +#include + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of touch, stylus and tool buttons. */ +class TouchButtonAccumulator { +public: + TouchButtonAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + int32_t getToolType() const; + bool isToolActive() const; + bool isHovering() const; + bool hasStylus() const; + +private: + bool mHaveBtnTouch; + bool mHaveStylus; + + bool mBtnTouch; + bool mBtnStylus; + bool mBtnStylus2; + bool mBtnToolFinger; + bool mBtnToolPen; + bool mBtnToolRubber; + bool mBtnToolBrush; + bool mBtnToolPencil; + bool mBtnToolAirbrush; + bool mBtnToolMouse; + bool mBtnToolLens; + bool mBtnToolDoubleTap; + bool mBtnToolTripleTap; + bool mBtnToolQuadTap; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H \ No newline at end of file diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index a9088bcf28..2153108186 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -14,16 +14,16 @@ * limitations under the License. */ -#include "InputReader.h" -#include "CursorInputMapper.h" -#include "InputDevice.h" -#include "InputMapper.h" -#include "KeyboardInputMapper.h" -#include "MultiTouchInputMapper.h" -#include "SingleTouchInputMapper.h" -#include "SwitchInputMapper.h" -#include "TestInputListener.h" -#include "TouchInputMapper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -- cgit v1.2.3-59-g8ed1b From f1fbf9efdb1b1f5af6f9b5be7d15a43c1c7ce34b Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 4 Sep 2019 16:29:40 -0700 Subject: Add override keyword to in inherited classes We add the override keyword to methods of a subclass that are meant to override those in the base class to make sure the method signatures match at compile time. Bug: 130819454 Test: m -j Change-Id: Ib325da3f9a60bfb77838fd37e44daf8f7c98e7ab --- services/inputflinger/reader/include/EventHub.h | 71 +++++++++++----------- services/inputflinger/reader/include/InputReader.h | 56 +++++++++-------- .../inputflinger/reader/mapper/CursorInputMapper.h | 19 +++--- .../reader/mapper/ExternalStylusInputMapper.h | 16 ++--- .../reader/mapper/JoystickInputMapper.h | 13 ++-- .../reader/mapper/KeyboardInputMapper.h | 27 ++++---- .../reader/mapper/MultiTouchInputMapper.h | 4 +- .../reader/mapper/RotaryEncoderInputMapper.h | 13 ++-- .../reader/mapper/SingleTouchInputMapper.h | 4 +- .../inputflinger/reader/mapper/SwitchInputMapper.h | 8 +-- .../inputflinger/reader/mapper/TouchInputMapper.h | 31 +++++----- .../reader/mapper/VibratorInputMapper.h | 17 +++--- 12 files changed, 146 insertions(+), 133 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index af83d2da67..c17f3a184d 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -260,61 +260,64 @@ class EventHub : public EventHubInterface { public: EventHub(); - virtual uint32_t getDeviceClasses(int32_t deviceId) const; + virtual uint32_t getDeviceClasses(int32_t deviceId) const override; - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const; + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override; - virtual int32_t getDeviceControllerNumber(int32_t deviceId) const; + virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override; - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override; virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const; + RawAbsoluteAxisInfo* outAxisInfo) const override; - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override; - virtual bool hasInputProperty(int32_t deviceId, int property) const; + virtual bool hasInputProperty(int32_t deviceId, int property) const override; virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, - uint32_t* outFlags) const; + uint32_t* outFlags) const override; - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, + AxisInfo* outAxisInfo) const override; - virtual void setExcludedDevices(const std::vector& devices); + virtual void setExcludedDevices(const std::vector& devices) override; - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; + virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override; + virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override; + virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const override; virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const; + uint8_t* outFlags) const override; - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); - virtual std::vector getVideoFrames(int32_t deviceId); + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override; + virtual std::vector getVideoFrames(int32_t deviceId) override; - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const; - virtual bool hasLed(int32_t deviceId, int32_t led) const; - virtual void setLedState(int32_t deviceId, int32_t led, bool on); + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override; + virtual bool hasLed(int32_t deviceId, int32_t led) const override; + virtual void setLedState(int32_t deviceId, int32_t led, bool on) override; - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector& outVirtualKeys) const; + virtual void getVirtualKeyDefinitions( + int32_t deviceId, std::vector& outVirtualKeys) const override; - virtual sp getKeyCharacterMap(int32_t deviceId) const; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map); + virtual sp getKeyCharacterMap(int32_t deviceId) const override; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, + const sp& map) override; - virtual void vibrate(int32_t deviceId, nsecs_t duration); - virtual void cancelVibrate(int32_t deviceId); + virtual void vibrate(int32_t deviceId, nsecs_t duration) override; + virtual void cancelVibrate(int32_t deviceId) override; - virtual void requestReopenDevices(); + virtual void requestReopenDevices() override; - virtual void wake(); + virtual void wake() override; - virtual void dump(std::string& dump); - virtual void monitor(); + virtual void dump(std::string& dump) override; + virtual void monitor() override; - virtual ~EventHub(); + virtual ~EventHub() override; private: struct Device { @@ -383,9 +386,9 @@ private: void configureFd(Device* device); - bool isDeviceEnabled(int32_t deviceId); - status_t enableDevice(int32_t deviceId); - status_t disableDevice(int32_t deviceId); + bool isDeviceEnabled(int32_t deviceId) override; + status_t enableDevice(int32_t deviceId) override; + status_t disableDevice(int32_t deviceId) override; status_t registerFdForEpoll(int fd); status_t unregisterFdFromEpoll(int fd); status_t registerDeviceForEpollLocked(Device* device); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 4288069659..7b4321ea82 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -52,31 +52,33 @@ public: const sp& listener); virtual ~InputReader(); - virtual void dump(std::string& dump); - virtual void monitor(); + virtual void dump(std::string& dump) override; + virtual void monitor() override; - virtual void loopOnce(); + virtual void loopOnce() override; - virtual void getInputDevices(std::vector& outInputDevices); + virtual void getInputDevices(std::vector& outInputDevices) override; - virtual bool isInputDeviceEnabled(int32_t deviceId); + virtual bool isInputDeviceEnabled(int32_t deviceId) override; - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode); - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw); + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) override; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) override; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - virtual void toggleCapsLockState(int32_t deviceId); + virtual void toggleCapsLockState(int32_t deviceId) override; virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual void requestRefreshConfiguration(uint32_t changes); + virtual void requestRefreshConfiguration(uint32_t changes) override; virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t deviceId, int32_t token); + ssize_t repeat, int32_t token) override; + virtual void cancelVibrate(int32_t deviceId, int32_t token) override; - virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); + virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override; protected: // These members are protected so they can be instrumented by test cases. @@ -90,20 +92,20 @@ protected: public: explicit ContextImpl(InputReader* reader); - virtual void updateGlobalMetaState(); - virtual int32_t getGlobalMetaState(); - virtual void disableVirtualKeysUntil(nsecs_t time); + virtual void updateGlobalMetaState() override; + virtual int32_t getGlobalMetaState() override; + virtual void disableVirtualKeysUntil(nsecs_t time) override; virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode); - virtual void fadePointer(); - virtual void requestTimeoutAtTime(nsecs_t when); - virtual int32_t bumpGeneration(); - virtual void getExternalStylusDevices(std::vector& outDevices); - virtual void dispatchExternalStylusState(const StylusState& outState); - virtual InputReaderPolicyInterface* getPolicy(); - virtual InputListenerInterface* getListener(); - virtual EventHubInterface* getEventHub(); - virtual uint32_t getNextSequenceNum(); + int32_t scanCode) override; + virtual void fadePointer() override; + virtual void requestTimeoutAtTime(nsecs_t when) override; + virtual int32_t bumpGeneration() override; + virtual void getExternalStylusDevices(std::vector& outDevices) override; + virtual void dispatchExternalStylusState(const StylusState& outState) override; + virtual InputReaderPolicyInterface* getPolicy() override; + virtual InputListenerInterface* getListener() override; + virtual EventHubInterface* getEventHub() override; + virtual uint32_t getNextSequenceNum() override; } mContext; friend class ContextImpl; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 52e42a7991..77d122af0a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -56,18 +56,19 @@ public: explicit CursorInputMapper(InputDevice* device); virtual ~CursorInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - virtual void fadePointer(); + virtual void fadePointer() override; - virtual std::optional getAssociatedDisplayId(); + virtual std::optional getAssociatedDisplayId() override; private: // Amount that trackball needs to move in order to generate a key event. diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 9764fbb3c1..34f339a3cd 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -30,13 +30,13 @@ public: explicit ExternalStylusInputMapper(InputDevice* device); virtual ~ExternalStylusInputMapper() = default; - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; @@ -44,6 +44,8 @@ private: TouchButtonAccumulator mTouchButtonAccumulator; StylusState mStylusState; + + void sync(nsecs_t when); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index 1b071d0480..b46d27d2d1 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -26,12 +26,13 @@ public: explicit JoystickInputMapper(InputDevice* device); virtual ~JoystickInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: struct Axis { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 2665c0258c..de2a377e9a 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -26,21 +26,22 @@ public: KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); virtual ~KeyboardInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - virtual std::optional getAssociatedDisplayId(); + virtual int32_t getMetaState() override; + virtual void updateMetaState(int32_t keyCode) override; + virtual std::optional getAssociatedDisplayId() override; private: // The current viewport. diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 87841ebeec..a45c3cb12b 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -94,8 +94,8 @@ public: explicit MultiTouchInputMapper(InputDevice* device); virtual ~MultiTouchInputMapper(); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; protected: virtual void syncTouch(nsecs_t when, RawState* outState); diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 26488373bd..38c7258f7e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -27,12 +27,13 @@ public: explicit RotaryEncoderInputMapper(InputDevice* device); virtual ~RotaryEncoderInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: CursorScrollAccumulator mRotaryEncoderScrollAccumulator; diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h index d6b1455b68..8438eee80b 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -27,8 +27,8 @@ public: explicit SingleTouchInputMapper(InputDevice* device); virtual ~SingleTouchInputMapper(); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; protected: virtual void syncTouch(nsecs_t when, RawState* outState); diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index dd4bb9ed65..e65d4e2bd5 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -26,11 +26,11 @@ public: explicit SwitchInputMapper(InputDevice* device); virtual ~SwitchInputMapper(); - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void process(const RawEvent* rawEvent) override; - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override; + virtual void dump(std::string& dump) override; private: uint32_t mSwitchValues; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index cf6cd76b87..89c017d84e 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -135,23 +135,24 @@ public: explicit TouchInputMapper(InputDevice* device); virtual ~TouchInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional getAssociatedDisplayId(); + virtual void fadePointer() override; + virtual void cancelTouch(nsecs_t when) override; + virtual void timeoutExpired(nsecs_t when) override; + virtual void updateExternalStylusState(const StylusState& state) override; + virtual std::optional getAssociatedDisplayId() override; protected: CursorButtonAccumulator mCursorButtonAccumulator; diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index 6b33f4811e..dc67890a31 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -26,14 +26,15 @@ public: explicit VibratorInputMapper(InputDevice* device); virtual ~VibratorInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void process(const RawEvent* rawEvent) override; + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) override; + virtual void cancelVibrate(int32_t token) override; + virtual void timeoutExpired(nsecs_t when) override; + virtual void dump(std::string& dump) override; private: bool mVibrating; -- cgit v1.2.3-59-g8ed1b From 6cd19a422afe967eb20c5c3eee2b36a29f52c495 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Fri, 30 Aug 2019 19:04:12 +0800 Subject: Make sure that touches are within the surface. Changed TouchInputMapper::isPointInsideSurface should check the surface range instead of physical frame range. Test: atest inputflinger_tests Test: enable emulator, open second display with landscape mode, try YouTube and changed it to portrait mode. Bug: 139805619 Change-Id: Id4ad7053acac33f49363442bd5f710f8023d5190 --- .../reader/mapper/TouchInputMapper.cpp | 4 +-- services/inputflinger/tests/InputReader_test.cpp | 40 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 6bd0ea959c..34603b95f4 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -3621,9 +3621,9 @@ bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { const float scaledX = x * mXScale; const float scaledY = y * mYScale; return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + scaledX >= mSurfaceLeft && scaledX <= mSurfaceLeft + mSurfaceWidth && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; + scaledY >= mSurfaceTop && scaledY <= mSurfaceTop + mSurfaceHeight; } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 2153108186..31b1652806 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -202,6 +202,20 @@ public: mConfig.setDisplayViewports(mViewports); } + bool updateViewport(const DisplayViewport& viewport) { + size_t count = mViewports.size(); + for (size_t i = 0; i < count; i++) { + const DisplayViewport& currentViewport = mViewports[i]; + if (currentViewport.displayId == viewport.displayId) { + mViewports[i] = viewport; + mConfig.setDisplayViewports(mViewports); + return true; + } + } + // no viewport found. + return false; + } + void addExcludedDeviceName(const std::string& deviceName) { mConfig.excludedDeviceNames.push_back(deviceName); } @@ -6593,4 +6607,30 @@ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId); } +/** + * Test touch should not work if outside of surface. + */ +TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + // Let surface be different from physical display. + std::optional internalViewport = + mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + internalViewport->logicalLeft = internalViewport->physicalTop + 20; + internalViewport->logicalTop = internalViewport->physicalRight + 20; + internalViewport->logicalRight = internalViewport->physicalRight - 20; + internalViewport->logicalBottom = internalViewport->physicalBottom - 20; + mFakePolicy->updateViewport(internalViewport.value()); + + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + int32_t rawX = 10; + int32_t rawY = 10; + processPosition(mapper, rawX, rawY); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From de4bf150c5685643daa1fa17f697e8b1cf3ea9ec Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 16 Aug 2019 11:12:52 -0500 Subject: Do not report latency for injected events When reporting the latency of touch events, ensure that injected events are excluded. These events can have arbitrary timestamps, and would not result in meaningful data. Also do not report events for statistics if inputfilter is enabled. Move the statistics reporting from InputTransport to InputDispatcher. This ensures that there's only 1 instance of the mStatistics object. This also provides easy access to the inputfilterenabled state. Bug: 13894199 Test: Change the reporting period to 0 (to report every event immediately) Inject events in various ways and ensure they don't go to statsd $ m statsd_testdrive && ./out/host/linux-x86/bin/statsd_testdrive 34 $ adb shell input tap 100 100 $ adb shell monkey 1000 Next, relaunch the statsd_testdrive script and touch the screen Observe that events are reported. Change-Id: Ief8040599a347e084e75584ed3164c60a6dbc4ad --- include/input/InputTransport.h | 5 ---- libs/input/Android.bp | 1 - libs/input/InputTransport.cpp | 15 ----------- libs/input/LatencyStatistics.cpp | 2 +- services/inputflinger/Android.bp | 1 + services/inputflinger/dispatcher/Android.bp | 1 + services/inputflinger/dispatcher/Entry.h | 18 +++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 30 ++++++++++++++++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 5 ++++ services/inputflinger/dispatcher/InputState.h | 3 --- 10 files changed, 56 insertions(+), 25 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 28b8d80074..c056c972d2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -35,7 +35,6 @@ #include #include -#include #include #include #include @@ -289,12 +288,8 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: - static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; sp mChannel; - LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; - - void reportTouchEventForStatistics(nsecs_t evdevTime); }; /* diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 7749e66c4d..8efaf3d90b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -57,7 +57,6 @@ cc_library { "libutils", "libbinder", "libui", - "libstatslog", ], sanitize: { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 8a2fc2a2d0..366c93cf1f 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -34,7 +34,6 @@ #include #include -#include using android::base::StringPrintf; @@ -538,9 +537,6 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } - if (source == AINPUT_SOURCE_TOUCHSCREEN) { - reportTouchEventForStatistics(eventTime); - } return mChannel->sendMessage(&msg); } @@ -567,17 +563,6 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle return OK; } -void InputPublisher::reportTouchEventForStatistics(nsecs_t evdevTime) { - if (mTouchStatistics.shouldReport()) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), - mTouchStatistics.getMax(), mTouchStatistics.getMean(), - mTouchStatistics.getStDev(), mTouchStatistics.getCount()); - mTouchStatistics.reset(); - } - nsecs_t latency = nanoseconds_to_microseconds(systemTime(CLOCK_MONOTONIC) - evdevTime); - mTouchStatistics.addValue(latency); -} - // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp index e343578e00..394da22a62 100644 --- a/libs/input/LatencyStatistics.cpp +++ b/libs/input/LatencyStatistics.cpp @@ -84,7 +84,7 @@ void LatencyStatistics::reset() { bool LatencyStatistics::shouldReport() { std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; - return mCount != 0 && timeSinceReport > mReportPeriod; + return mCount != 0 && timeSinceReport >= mReportPeriod; } } // namespace android diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 11578c393e..f6b5935bcf 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -44,6 +44,7 @@ cc_library_shared { "libhidlbase", "libinput", "liblog", + "libstatslog", "libutils", "libui", "server_configurable_flags", diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b8c3a808f1..9185e00272 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "libinputreporter", "libinputflinger_base", "liblog", + "libstatslog", "libui", "libutils", ], diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a9e22f19f1..28c2799477 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -29,6 +29,9 @@ namespace android::inputdispatcher { +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; + struct EventEntry { enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; @@ -41,8 +44,23 @@ struct EventEntry { bool dispatchInProgress; // initially false, set to true while dispatching + /** + * Injected keys are events from an external (probably untrusted) application + * and are not related to real hardware state. They come in via + * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED. + */ inline bool isInjected() const { return injectionState != nullptr; } + /** + * Synthesized events are either injected events, or events that come + * from real hardware, but aren't directly attributable to a specific hardware event. + * Key repeat is a synthesized event, because it is related to an actual hardware state + * (a key is currently pressed), but the repeat itself is generated by the framework. + */ + inline bool isSynthesized() const { + return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM; + } + void release(); virtual void appendDescription(std::string& msg) const = 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 039462e23b..2361867e62 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -2274,6 +2275,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); + reportTouchEventForStatistics(*motionEntry); break; } @@ -4478,6 +4480,34 @@ void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventE // TODO Write some statistics about how long we spend waiting. } +/** + * Report the touch event latency to the statsd server. + * Input events are reported for statistics if: + * - This is a touchscreen event + * - InputFilter is not enabled + * - Event is not injected or synthesized + * + * Statistics should be reported before calling addValue, to prevent a fresh new sample + * from getting aggregated with the "old" data. + */ +void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry) + REQUIRES(mLock) { + const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) && + !(motionEntry.isSynthesized()) && !mInputFilterEnabled; + if (!reportForStatistics) { + return; + } + + if (mTouchStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), + mTouchStatistics.getMax(), mTouchStatistics.getMean(), + mTouchStatistics.getStDev(), mTouchStatistics.getCount()); + mTouchStatistics.reset(); + } + const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime); + mTouchStatistics.addValue(latencyMicros); +} + void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { ATRACE_INT("iq", mInboundQueue.size()); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index f2c04028e4..0d9d6b0c15 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -455,6 +456,10 @@ private: void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Statistics gathering. + static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; + LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; + + void reportTouchEventForStatistics(const MotionEntry& entry); void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); void traceInboundQueueLengthLocked() REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index bccef0fca3..47e9b36219 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -24,9 +24,6 @@ namespace android::inputdispatcher { -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - /* Tracks dispatched key and motion event state so that cancellation events can be * synthesized when events are dropped. */ class InputState { -- cgit v1.2.3-59-g8ed1b From 86587285cbd5292b28ee51d42210b50623c3f9f3 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 9 Sep 2019 18:20:15 +0100 Subject: Use static bool instead of define The defines for debugging prevent compilers from compiling the code, as well as make the code harder to read due to identation. Using static bools helps ensure that the code inside the debugging logic compiles, and a decent compiler should be able to remove the dead code anyways. Test: adb logcat and inspect the InputDispatcher logs for when debugging is true Bug: 70668286 Change-Id: If1a057da4193f965e35d2ca7c1bf39249b9222fa --- .../inputflinger/dispatcher/InputDispatcher.cpp | 261 +++++++++++---------- 1 file changed, 132 insertions(+), 129 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 039462e23b..e161e8c29c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,7 +35,7 @@ #define DEBUG_INJECTION 0 // Log debug messages about input focus tracking. -#define DEBUG_FOCUS 0 +static constexpr bool DEBUG_FOCUS = false; // Log debug messages about the app switch latency optimization. #define DEBUG_APP_SWITCH 0 @@ -313,9 +313,9 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // If dispatching is frozen, do not process timeouts or try to deliver any new events. if (mDispatchFrozen) { -#if DEBUG_FOCUS - ALOGD("Dispatch frozen. Waiting some more."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dispatch frozen. Waiting some more."); + } return; } @@ -1041,11 +1041,11 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { -#if DEBUG_FOCUS - ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event delivery to target with channel '%s' because it " + "is no longer registered with the input dispatcher.", + inputTarget.inputChannel->getName().c_str()); + } } } } @@ -1056,9 +1056,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { if (applicationHandle == nullptr && windowHandle == nullptr) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Waiting for system to become ready for input. Reason: %s", reason); + } mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = LONG_LONG_MAX; @@ -1067,10 +1067,10 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + } nsecs_t timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); @@ -1159,9 +1159,9 @@ nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t current } void InputDispatcher::resetANRTimeoutsLocked() { -#if DEBUG_FOCUS - ALOGD("Resetting ANR timeouts."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Resetting ANR timeouts."); + } // Reset input target wait timeout. mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; @@ -1253,11 +1253,11 @@ Failed: Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); -#endif + if (DEBUG_FOCUS) { + ALOGD("findFocusedWindow finished: injectionResult=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, timeSpentWaitingForApplication / 1000000.0); + } return injectionResult; } @@ -1308,11 +1308,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { -#if DEBUG_FOCUS - ALOGD("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because a pointer for a different device is already down " + "in display %" PRId32, + displayId); + } // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_FAILED; switchedDevice = false; @@ -1326,11 +1326,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { -#if DEBUG_FOCUS - ALOGI("Dropping move event because a pointer for a different device is already active " - "in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGI("Dropping move event because a pointer for a different device is already active " + "in display %" PRId32, + displayId); + } // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; switchedDevice = false; @@ -1420,11 +1420,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // If the pointer is not currently down, then ignore the event. if (!mTempTouchState.down) { -#if DEBUG_FOCUS - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display %" PRId32, + displayId); + } injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1441,11 +1441,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, findTouchedWindowAtLocked(displayId, x, y); if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, - oldTouchedWindowHandle->getName().c_str(), - newTouchedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, + oldTouchedWindowHandle->getName().c_str(), + newTouchedWindowHandle->getName().c_str(), displayId); + } // Make a slippery exit from the old window. mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, @@ -1514,11 +1514,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); if (!haveForegroundWindow && !hasGestureMonitor) { -#if DEBUG_FOCUS - ALOGD("Dropping event because there is no touched foreground window in display %" PRId32 - " or gesture monitor to receive it.", - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because there is no touched foreground window in display " + "%" PRId32 " or gesture monitor to receive it.", + displayId); + } injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1622,18 +1622,19 @@ Failed: if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (!wrongDevice) { if (switchedDevice) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Switched to a different device."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Switched to a different device."); + } *outConflictingPointerActions = true; } if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Hover received while pointer was down."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Hover received while pointer was " + "down."); + } *outConflictingPointerActions = true; } mTempTouchState.reset(); @@ -1650,9 +1651,9 @@ Failed: } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Down received while already down."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Down received while already down."); + } *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -1693,9 +1694,9 @@ Failed: mLastHoverWindowHandle = newHoverWindowHandle; } } else { -#if DEBUG_FOCUS - ALOGD("Not updating touch focus because injection was denied."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Not updating touch focus because injection was denied."); + } } Unresponsive: @@ -1704,11 +1705,11 @@ Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); -#endif + if (DEBUG_FOCUS) { + ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); + } return injectionResult; } @@ -2007,10 +2008,11 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (!splitMotionEntry) { return; // split event was dropped } -#if DEBUG_FOCUS - ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); - logOutboundMotionDetails(" ", splitMotionEntry); -#endif + if (DEBUG_FOCUS) { + ALOGD("channel '%s' ~ Split motion event.", + connection->getInputChannelName().c_str()); + logOutboundMotionDetails(" ", splitMotionEntry); + } enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); splitMotionEntry->release(); return; @@ -3252,9 +3254,13 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, int32_t displayId, const sp& setInputWindowsListener) { -#if DEBUG_FOCUS - ALOGD("setInputWindows displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + std::string windowList; + for (const sp& iwh : inputWindowHandles) { + windowList += iwh->getName() + " "; + } + ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3286,10 +3292,10 @@ void InputDispatcher::setInputWindows(const std::vector>& if (oldFocusedWindowHandle != newFocusedWindowHandle) { if (oldFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); + } sp focusedInputChannel = getInputChannelLocked(oldFocusedWindowHandle->getToken()); if (focusedInputChannel != nullptr) { @@ -3300,10 +3306,10 @@ void InputDispatcher::setInputWindows(const std::vector>& mFocusedWindowHandlesByDisplay.erase(displayId); } if (newFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); + } mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; } @@ -3318,10 +3324,10 @@ void InputDispatcher::setInputWindows(const std::vector>& for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { -#if DEBUG_FOCUS - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); + } sp touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { @@ -3343,9 +3349,9 @@ void InputDispatcher::setInputWindows(const std::vector>& // which might not happen until the next GC. for (const sp& oldWindowHandle : oldWindowHandles) { if (!hasWindowHandleLocked(oldWindowHandle)) { -#if DEBUG_FOCUS - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + } oldWindowHandle->releaseChannel(); } } @@ -3361,9 +3367,10 @@ void InputDispatcher::setInputWindows(const std::vector>& void InputDispatcher::setFocusedApplication( int32_t displayId, const sp& inputApplicationHandle) { -#if DEBUG_FOCUS - ALOGD("setFocusedApplication displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId, + inputApplicationHandle ? inputApplicationHandle->getName().c_str() : ""); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3381,10 +3388,6 @@ void InputDispatcher::setFocusedApplication( oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } - -#if DEBUG_FOCUS - // logDispatchStateLocked(); -#endif } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3401,9 +3404,9 @@ void InputDispatcher::setFocusedApplication( * display. The display-specified events won't be affected. */ void InputDispatcher::setFocusedDisplay(int32_t displayId) { -#if DEBUG_FOCUS - ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3442,9 +3445,9 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3452,9 +3455,9 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { -#if DEBUG_FOCUS - ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); -#endif + if (DEBUG_FOCUS) { + ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); + } bool changed; { // acquire lock @@ -3476,9 +3479,9 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { changed = false; } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock if (changed) { @@ -3488,9 +3491,9 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } void InputDispatcher::setInputFilterEnabled(bool enabled) { -#if DEBUG_FOCUS - ALOGD("setInputFilterEnabled: enabled=%d", enabled); -#endif + if (DEBUG_FOCUS) { + ALOGD("setInputFilterEnabled: enabled=%d", enabled); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3509,9 +3512,9 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp& toToken) { if (fromToken == toToken) { -#if DEBUG_FOCUS - ALOGD("Trivial transfer to same window."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Trivial transfer to same window."); + } return true; } @@ -3524,14 +3527,14 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< ALOGW("Cannot transfer focus because from or to window not found."); return false; } -#if DEBUG_FOCUS - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); + } if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { -#if DEBUG_FOCUS - ALOGD("Cannot transfer focus because windows are on different displays."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Cannot transfer focus because windows are on different displays."); + } return false; } @@ -3559,9 +3562,9 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< Found: if (!found) { -#if DEBUG_FOCUS - ALOGD("Focus transfer failed because from window did not have focus."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus transfer failed because from window did not have focus."); + } return false; } @@ -3577,9 +3580,9 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< synthesizeCancelationEventsForConnectionLocked(fromConnection, options); } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3588,9 +3591,9 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< } void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { -#if DEBUG_FOCUS - ALOGD("Resetting and dropping all events (%s).", reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Resetting and dropping all events (%s).", reason); + } CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); -- cgit v1.2.3-59-g8ed1b From 2d0e948f83f508407c39e9c298f6c80783958152 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Sep 2019 12:52:47 +0100 Subject: Hold wakelock while handling device add/remove EventHub is using epoll (with inotify) to monitor for input devices being added or removed. This is done by setting a watch on the directory /dev/input. After the device is added or removed, epoll_wait would return, and the code would proceed to handle the event. To ensure that the system is awake the entire time an input device is being added or removed, use EPOLLWAKEUP for the inotify fd. Bug: 141532209 Test: none Change-Id: Ie23ef459bdecdf1df929c34781165e63cae313bf --- services/inputflinger/reader/EventHub.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index c15ecfd66b..efe3809f1d 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -271,9 +271,8 @@ EventHub::EventHub(void) ALOGI("Video device scanning disabled"); } - struct epoll_event eventItem; - memset(&eventItem, 0, sizeof(eventItem)); - eventItem.events = EPOLLIN; + struct epoll_event eventItem = {}; + eventItem.events = EPOLLIN | EPOLLWAKEUP; eventItem.data.fd = mINotifyFd; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); -- cgit v1.2.3-59-g8ed1b From 7d3847311e3d3955ea91facb0b83b824e16efdd4 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 25 Sep 2019 15:07:32 -0700 Subject: getDisplayViewportByType doesn't count more than one viewport Bug: none Test: none Change-Id: I795f14bb1d7bd7f62a0d2a6eb301f3cced4f5109 --- services/inputflinger/InputReaderBase.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index bc53cf52cc..0422d8342b 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -114,8 +114,10 @@ std::optional InputReaderConfiguration::getDisplayViewportByTyp std::optional result = std::nullopt; for (const DisplayViewport& currentViewport : mDisplays) { // Return the first match - if (currentViewport.type == type && !result) { - result = std::make_optional(currentViewport); + if (currentViewport.type == type) { + if (!result) { + result = std::make_optional(currentViewport); + } count++; } } -- cgit v1.2.3-59-g8ed1b From e07105869c79c6ea2844310a45373eeb7b5c4cfa Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 26 Sep 2019 13:46:12 -0700 Subject: Remove logspam This log fires constantly on a device that appears to have touch that's working perfectly fine. So it seems to be an ALOGV and not actually an error that needs to be logged. Also turn off ALOGV's and fix an unused variable error Test: adb logcat Change-Id: I7c66b76c6c47b22c0317ff98556873bd4d351d95 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index e161e8c29c..8dcf1e0692 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "InputDispatcher" #define ATRACE_TAG ATRACE_TAG_INPUT -#define LOG_NDEBUG 0 +#define LOG_NDEBUG 1 // Log detailed debug messages about each inbound event notification to the dispatcher. #define DEBUG_INBOUND_EVENT_DETAILS 0 @@ -3219,7 +3219,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); if (canReceiveInput && !noInputChannel) { - ALOGE("Window handle %s has no registered input channel", + ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); } continue; @@ -3954,7 +3954,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i return BAD_VALUE; } - const bool removed = removeByValue(mConnectionsByFd, connection); + [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); ALOG_ASSERT(removed); mInputChannelsByToken.erase(inputChannel->getToken()); -- cgit v1.2.3-59-g8ed1b From 372fbe4ee9b2bf80d1b7c4daeed47be6e2eb3a9c Mon Sep 17 00:00:00 2001 From: Jaewan Kim Date: Wed, 2 Oct 2019 10:58:46 +0900 Subject: InputDispatcher: Fix typo Test: Build Change-Id: I500c732808cb2db6cd0669ee1eebbc9a1f150876 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 039462e23b..b8970bf948 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2768,7 +2768,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { "yCursorPosition=%f, downTime=%" PRId64, args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, + args->edgeFlags, args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " -- cgit v1.2.3-59-g8ed1b From 7a522bf597fe680dd74b7fc0c8dfead2cd7fe781 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Sep 2019 12:46:29 +0100 Subject: Ensure input has CAP_BLOCK_SUSPEND The process that's running the input code (currently, SYSTEM_SERVER) should be able to prevent suspend from happening. This is to make sure that we can always process the touch events. Since the kernel is not able to enforce the permissions for CAP_BLOCK_SUSPEND when EPOLLWAKEUP is being used, and just silently fails (see http://man7.org/linux/man-pages/man2/epoll_ctl.2.html and specifically the "BUGS" section), we should really make sure that the current process is able to hold the wakelock. Bug: 141532209 Test: boot up the phone and ensure it doesn't crash Change-Id: Ibde183cf7a41dd5369873093be373c10dd580115 --- services/inputflinger/reader/Android.bp | 1 + services/inputflinger/reader/EventHub.cpp | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index a64f4ddc47..3c1607096e 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { shared_libs: [ "libbase", + "libcap", "libinputflinger_base", "libcrypto", "libcutils", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index efe3809f1d..c8da0ab29f 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -237,6 +238,47 @@ bool EventHub::Device::hasValidFd() { return !isVirtual && enabled; } +/** + * Get the capabilities for the current process. + * Crashes the system if unable to create / check / destroy the capabilities object. + */ +class Capabilities final { +public: + explicit Capabilities() { + mCaps = cap_get_proc(); + LOG_ALWAYS_FATAL_IF(mCaps == nullptr, "Could not get capabilities of the current process"); + } + + /** + * Check whether the current process has a specific capability + * in the set of effective capabilities. + * Return CAP_SET if the process has the requested capability + * Return CAP_CLEAR otherwise. + */ + cap_flag_value_t checkEffectiveCapability(cap_value_t capability) { + cap_flag_value_t value; + const int result = cap_get_flag(mCaps, capability, CAP_EFFECTIVE, &value); + LOG_ALWAYS_FATAL_IF(result == -1, "Could not obtain the requested capability"); + return value; + } + + ~Capabilities() { + const int result = cap_free(mCaps); + LOG_ALWAYS_FATAL_IF(result == -1, "Could not release the capabilities structure"); + } + +private: + cap_t mCaps; +}; + +static void ensureProcessCanBlockSuspend() { + Capabilities capabilities; + const bool canBlockSuspend = + capabilities.checkEffectiveCapability(CAP_BLOCK_SUSPEND) == CAP_SET; + LOG_ALWAYS_FATAL_IF(!canBlockSuspend, + "Input must be able to block suspend to properly process events"); +} + // --- EventHub --- const int EventHub::EPOLL_MAX_EVENTS; @@ -253,6 +295,7 @@ EventHub::EventHub(void) mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { + ensureProcessCanBlockSuspend(); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create1(EPOLL_CLOEXEC); -- cgit v1.2.3-59-g8ed1b From 4b5a87fa938c25c496937fd8c25c9c515e6cf5dd Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Sep 2019 13:03:58 +0100 Subject: Remove wakelocks from InputReader The flag EPOLLWAKEUP already guarantees that the system stays awake while we are processing any events returned by epoll_wait. The system may only enter suspend after epoll_wait is called again, or if the fd is removed from epoll. So, there is no need to do the same wakelock management explicitly in EventHub/InputReader. Bug: 141532209 Test: use the power key to turn off/on screen on the device. Play some music (download some mp3 file), then turn off the screen, and use volume keys to change the music volume. Change-Id: I8b3e64300d4f523e28c566cd7d7cd8aef97e0fed --- services/inputflinger/reader/Android.bp | 1 - services/inputflinger/reader/EventHub.cpp | 28 ++++++++++------------------ 2 files changed, 10 insertions(+), 19 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 3c1607096e..23c08b25f3 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -58,7 +58,6 @@ cc_library_shared { "liblog", "libui", "libutils", - "libhardware_legacy", ], header_libs: [ diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index c8da0ab29f..264d2879e4 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -37,8 +37,6 @@ #include "EventHub.h" -#include - #include #include #include @@ -71,7 +69,6 @@ namespace android { static constexpr bool DEBUG = false; -static const char* WAKE_LOCK_ID = "KeyEvents"; static const char* DEVICE_PATH = "/dev/input"; // v4l2 devices go directly into /dev static const char* VIDEO_DEVICE_PATH = "/dev"; @@ -296,7 +293,6 @@ EventHub::EventHub(void) mPendingEventIndex(0), mPendingINotify(false) { ensureProcessCanBlockSuspend(); - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create1(EPOLL_CLOEXEC); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); @@ -354,8 +350,6 @@ EventHub::~EventHub(void) { ::close(mINotifyFd); ::close(mWakeReadPipeFd); ::close(mWakeWritePipeFd); - - release_wake_lock(WAKE_LOCK_ID); } InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { @@ -1046,26 +1040,24 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz break; } - // Poll for events. Mind the wake lock dance! - // We hold a wake lock at all times except during epoll_wait(). This works due to some - // subtle choreography. When a device driver has pending (unread) events, it acquires - // a kernel wake lock. However, once the last pending event has been read, the device - // driver will release the kernel wake lock. To prevent the system from going to sleep - // when this happens, the EventHub holds onto its own user wake lock while the client - // is processing events. Thus the system can only sleep if there are no events - // pending or currently being processed. + // Poll for events. + // When a device driver has pending (unread) events, it acquires + // a kernel wake lock. Once the last pending event has been read, the device + // driver will release the kernel wake lock, but the epoll will hold the wakelock, + // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait + // is called again for the same fd that produced the event. + // Thus the system can only sleep if there are no events pending or + // currently being processed. // // The timeout is advisory only. If the device is asleep, it will not wake just to // service the timeout. mPendingEventIndex = 0; - mLock.unlock(); // release lock before poll, must be before release_wake_lock - release_wake_lock(WAKE_LOCK_ID); + mLock.unlock(); // release lock before poll int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock + mLock.lock(); // reacquire lock after poll if (pollResult == 0) { // Timed out. -- cgit v1.2.3-59-g8ed1b From bd93138afe956c0f7996acdebd384298b45ea765 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 9 Oct 2019 15:23:33 -0700 Subject: Remove unused function 'removeWindow' Next, we should rename 'removeWindowByToken' to 'removeWindow', because it is now obvious it is removing by token. Bug: none Test: none Change-Id: Ie53ac65809aa97e7fa4043b2d03b11d445bf53d7 --- services/inputflinger/dispatcher/TouchState.cpp | 9 --------- services/inputflinger/dispatcher/TouchState.h | 1 - 2 files changed, 10 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 18848a0c2f..2baceba582 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -93,15 +93,6 @@ void TouchState::addGestureMonitors(const std::vector& newMonito std::end(newMonitors)); } -void TouchState::removeWindow(const sp& windowHandle) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle == windowHandle) { - windows.erase(windows.begin() + i); - return; - } - } -} - void TouchState::removeWindowByToken(const sp& token) { for (size_t i = 0; i < windows.size(); i++) { if (windows[i].windowHandle->getToken() == token) { diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 3e0e0eb897..623c6a824f 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -49,7 +49,6 @@ struct TouchState { BitSet32 pointerIds); void addPortalWindow(const sp& windowHandle); void addGestureMonitors(const std::vector& monitors); - void removeWindow(const sp& windowHandle); void removeWindowByToken(const sp& token); void filterNonAsIsTouchWindows(); void filterNonMonitors(); -- cgit v1.2.3-59-g8ed1b From 7c34b23f5cb2ae1ebcc2cc72e120d9d2d7b0d6d8 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 11 Oct 2019 19:08:48 -0700 Subject: Remove unused parameter displayId The parameter is not currently used in InputDispatcher. Test: presubmit Bug: none Change-Id: I61f9e6566e4fe77318814e56e73b7f3470fa6a04 --- services/inputflinger/InputManager.cpp | 2 +- services/inputflinger/dispatcher/InputDispatcher.cpp | 6 ++---- services/inputflinger/dispatcher/InputDispatcher.h | 3 +-- services/inputflinger/dispatcher/include/InputDispatcherInterface.h | 3 +-- services/inputflinger/tests/InputDispatcher_test.cpp | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7d3067242a..e7640dd6af 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -128,7 +128,7 @@ void InputManager::registerInputChannel(const sp& channel) { "from non shell/root entity (PID: %d)", ipc->getCallingPid()); return; } - mDispatcher->registerInputChannel(channel, false); + mDispatcher->registerInputChannel(channel); } void InputManager::unregisterInputChannel(const sp& channel) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4db9ae2125..ee8c344a96 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3865,11 +3865,9 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector } } -status_t InputDispatcher::registerInputChannel(const sp& inputChannel, - int32_t displayId) { +status_t InputDispatcher::registerInputChannel(const sp& inputChannel) { #if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, - inputChannel->getName().c_str(), displayId); + ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str()); #endif { // acquire lock diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 0d9d6b0c15..1f906e43f6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -106,8 +106,7 @@ public: virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) override; - virtual status_t registerInputChannel(const sp& inputChannel, - int32_t displayId) override; + virtual status_t registerInputChannel(const sp& inputChannel) override; virtual status_t registerInputMonitor(const sp& inputChannel, int32_t displayId, bool isGestureMonitor) override; virtual status_t unregisterInputChannel(const sp& inputChannel) override; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 9329ca664e..ce7366f475 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -126,8 +126,7 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual status_t registerInputChannel(const sp& inputChannel, - int32_t displayId) = 0; + virtual status_t registerInputChannel(const sp& inputChannel) = 0; /* Registers input channels to be used to monitor input events. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index aa98ef78f5..7d69854868 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -471,7 +471,7 @@ public: FakeInputReceiver(dispatcher, name, displayId), mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) { mServerChannel->setToken(new BBinder()); - mDispatcher->registerInputChannel(mServerChannel, displayId); + mDispatcher->registerInputChannel(mServerChannel); inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); -- cgit v1.2.3-59-g8ed1b From d399a52883520fbff3880163e9d245fdc66b178f Mon Sep 17 00:00:00 2001 From: Ashwini Oruganti Date: Thu, 10 Oct 2019 11:16:45 -0700 Subject: Don't leak input events to dumpsys on user builds Add a check for ro.debuggable in before adding the details of KeyEvent and MotionEvent to logs. Bug: 139945049 Test: Tested on a device, the input functions work as expected and input event logs are not leaked to dumpsys on user builds. Change-Id: I98c9c375f18963177bf0c1d8829a217b4ad4acc6 --- services/inputflinger/dispatcher/Entry.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 640a69a2ec..930c7c77e7 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -18,10 +18,12 @@ #include "Connection.h" +#include #include #include #include +using android::base::GetBoolProperty; using android::base::StringPrintf; namespace android::inputdispatcher { @@ -133,7 +135,11 @@ KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, ui KeyEntry::~KeyEntry() {} void KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + msg += StringPrintf("KeyEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", deviceId, source, displayId, keyActionToString(action).c_str(), flags, @@ -189,7 +195,11 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device MotionEntry::~MotionEntry() {} void MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 + msg += StringPrintf("MotionEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " -- cgit v1.2.3-59-g8ed1b From 05de577c02e3167546ba4f2388ebc45cc781e2e8 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 26 Sep 2019 18:31:26 +0800 Subject: Fix foldable emulator touch does not work When foldable emulator change to minimized state and rotate screen, the physical display would align to right-top (rotate to 90) or left-bottom (rotate to 270), but the touch axes range still keep the initialed values, so when cookPointerData, it would also align to right-top or left-bottom when display rotated. - Fix the surface range when display had offset and rotated. - Fix the surface range checker function. Bug: 138708005 Test: atest inputflinger_tests Test: open foldable emulator, change to minimized state and rotate device, test if touch can work fine. Change-Id: Idc97107512dd1c11e3783eb3276635f69d3b047b --- .../reader/mapper/TouchInputMapper.cpp | 50 ++++++++++++++++------ .../inputflinger/reader/mapper/TouchInputMapper.h | 1 + services/inputflinger/tests/InputReader_test.cpp | 41 +++++++++++++----- 3 files changed, 68 insertions(+), 24 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 34603b95f4..c80a2dc4d9 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -668,7 +668,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalLeft = mViewport.deviceHeight - naturalPhysicalWidth; naturalPhysicalTop = mViewport.physicalLeft; naturalDeviceWidth = mViewport.deviceHeight; naturalDeviceHeight = mViewport.deviceWidth; @@ -689,7 +689,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceWidth - naturalPhysicalHeight; naturalDeviceWidth = mViewport.deviceHeight; naturalDeviceHeight = mViewport.deviceWidth; break; @@ -2173,6 +2173,7 @@ void TouchInputMapper::cookPointerData() { // TODO: Adjust coverage coords? float xTransformed = in.x, yTransformed = in.y; mAffineTransform.applyTo(xTransformed, yTransformed); + rotateAndScale(xTransformed, yTransformed); // Adjust X, Y, and coverage coords for surface orientation. float x, y; @@ -2180,8 +2181,8 @@ void TouchInputMapper::cookPointerData() { switch (mSurfaceOrientation) { case DISPLAY_ORIENTATION_90: - x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + x = yTransformed + mYTranslate; + y = xTransformed + mXTranslate; left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; @@ -2194,8 +2195,8 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; - y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + x = xTransformed + mXTranslate; + y = yTransformed + mYTranslate; left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; @@ -2208,8 +2209,8 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; - y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + x = yTransformed + mYTranslate; + y = xTransformed + mXTranslate; left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; @@ -2222,8 +2223,8 @@ void TouchInputMapper::cookPointerData() { } break; default: - x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + x = xTransformed + mXTranslate; + y = yTransformed + mYTranslate; left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; @@ -3617,13 +3618,34 @@ void TouchInputMapper::cancelTouch(nsecs_t when) { abortTouches(when, 0 /* policyFlags*/); } +void TouchInputMapper::rotateAndScale(float& x, float& y) { + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(mRawPointerAxes.x.maxValue - x) * mXScale; + y = float(y - mRawPointerAxes.y.minValue) * mYScale; + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawPointerAxes.x.maxValue - x) * mXScale; + y = float(mRawPointerAxes.y.maxValue - y) * mYScale; + break; + case DISPLAY_ORIENTATION_270: + x = float(x - mRawPointerAxes.x.minValue) * mXScale; + y = float(mRawPointerAxes.y.maxValue - y) * mYScale; + break; + default: + x = float(x - mRawPointerAxes.x.minValue) * mXScale; + y = float(y - mRawPointerAxes.y.minValue) * mYScale; + break; + } +} + bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - const float scaledX = x * mXScale; - const float scaledY = y * mYScale; + float xTransformed = x, yTransformed = y; + rotateAndScale(xTransformed, yTransformed); return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - scaledX >= mSurfaceLeft && scaledX <= mSurfaceLeft + mSurfaceWidth && + xTransformed >= mSurfaceLeft && xTransformed <= mSurfaceLeft + mSurfaceWidth && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - scaledY >= mSurfaceTop && scaledY <= mSurfaceTop + mSurfaceHeight; + yTransformed >= mSurfaceTop && yTransformed <= mSurfaceTop + mSurfaceHeight; } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 89c017d84e..4b1c0cbeaf 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -758,6 +758,7 @@ private: static void assignPointerIds(const RawState* last, RawState* current); const char* modeToString(DeviceMode deviceMode); + void rotateAndScale(float& x, float& y); }; } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 31b1652806..028fb6f0b2 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -6614,20 +6614,41 @@ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); - // Let surface be different from physical display. + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + // Touch on left-top area should work. + int32_t rawX = DISPLAY_WIDTH / 2 - 1; + int32_t rawY = DISPLAY_HEIGHT / 2 - 1; + processPosition(mapper, rawX, rawY); + processSync(mapper); + + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + + // Reset. + mapper->reset(ARBITRARY_TIME); + + // Let logical display be different to physical display and rotate 90-degrees. std::optional internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - internalViewport->logicalLeft = internalViewport->physicalTop + 20; - internalViewport->logicalTop = internalViewport->physicalRight + 20; - internalViewport->logicalRight = internalViewport->physicalRight - 20; - internalViewport->logicalBottom = internalViewport->physicalBottom - 20; + internalViewport->orientation = DISPLAY_ORIENTATION_90; + internalViewport->logicalLeft = 0; + internalViewport->logicalTop = 0; + internalViewport->logicalRight = DISPLAY_HEIGHT; + internalViewport->logicalBottom = DISPLAY_WIDTH / 2; + + internalViewport->physicalLeft = DISPLAY_HEIGHT; + internalViewport->physicalTop = DISPLAY_WIDTH / 2; + internalViewport->physicalRight = DISPLAY_HEIGHT; + internalViewport->physicalBottom = DISPLAY_WIDTH; + + internalViewport->deviceWidth = DISPLAY_HEIGHT; + internalViewport->deviceHeight = DISPLAY_WIDTH; mFakePolicy->updateViewport(internalViewport.value()); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - int32_t rawX = 10; - int32_t rawY = 10; + // Display align to right-top after rotate 90-degrees, touch on left-top area should not work. processPosition(mapper, rawX, rawY); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -- cgit v1.2.3-59-g8ed1b From 0fb1a0e904a10bd24fbbaa3324c94c26833e3925 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 22 Oct 2019 11:23:36 -0700 Subject: Use enum class for DropReason Using enum class provides type safety and helps ensure there are no missed switch/case statements at compile time. Bug: 70668286 Test: presubmit Change-Id: Ia3d761d2aec0694edcf8ce24643f17e25cd79a1b --- .../inputflinger/dispatcher/InputDispatcher.cpp | 67 +++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 15 +++-- 2 files changed, 41 insertions(+), 41 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index ee8c344a96..d8cf8ddeae 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -245,7 +245,7 @@ static bool removeByValue(std::unordered_map& map, const V& value) { InputDispatcher::InputDispatcher(const sp& policy) : mPolicy(policy), mPendingEvent(nullptr), - mLastDropReason(DROP_REASON_NOT_DROPPED), + mLastDropReason(DropReason::NOT_DROPPED), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), @@ -374,11 +374,11 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // All events are eventually dequeued and processed this way, even if we intend to drop them. ALOG_ASSERT(mPendingEvent != nullptr); bool done = false; - DropReason dropReason = DROP_REASON_NOT_DROPPED; + DropReason dropReason = DropReason::NOT_DROPPED; if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { - dropReason = DROP_REASON_POLICY; + dropReason = DropReason::POLICY; } else if (!mDispatchEnabled) { - dropReason = DROP_REASON_DISABLED; + dropReason = DropReason::DISABLED; } if (mNextUnblockedEvent == mPendingEvent) { @@ -390,14 +390,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { ConfigurationChangedEntry* typedEntry = static_cast(mPendingEvent); done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped + dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped break; } case EventEntry::TYPE_DEVICE_RESET: { DeviceResetEntry* typedEntry = static_cast(mPendingEvent); done = dispatchDeviceResetLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped + dropReason = DropReason::NOT_DROPPED; // device resets are never dropped break; } @@ -407,15 +407,15 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (isAppSwitchKeyEvent(typedEntry)) { resetPendingAppSwitchLocked(true); isAppSwitchDue = false; - } else if (dropReason == DROP_REASON_NOT_DROPPED) { - dropReason = DROP_REASON_APP_SWITCH; + } else if (dropReason == DropReason::NOT_DROPPED) { + dropReason = DropReason::APP_SWITCH; } } - if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DropReason::STALE; } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; + if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DropReason::BLOCKED; } done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; @@ -423,14 +423,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast(mPendingEvent); - if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { - dropReason = DROP_REASON_APP_SWITCH; + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; } - if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DropReason::STALE; } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; + if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DropReason::BLOCKED; } done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; @@ -442,7 +442,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } if (done) { - if (dropReason != DROP_REASON_NOT_DROPPED) { + if (dropReason != DropReason::NOT_DROPPED) { dropInboundEventLocked(mPendingEvent, dropReason); } mLastDropReason = dropReason; @@ -594,35 +594,36 @@ void InputDispatcher::addGestureMonitors(const std::vector& monitors, void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { const char* reason; switch (dropReason) { - case DROP_REASON_POLICY: + case DropReason::POLICY: #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("Dropped event because policy consumed it."); #endif reason = "inbound event was dropped because the policy consumed it"; break; - case DROP_REASON_DISABLED: - if (mLastDropReason != DROP_REASON_DISABLED) { + case DropReason::DISABLED: + if (mLastDropReason != DropReason::DISABLED) { ALOGI("Dropped event because input dispatch is disabled."); } reason = "inbound event was dropped because input dispatch is disabled"; break; - case DROP_REASON_APP_SWITCH: + case DropReason::APP_SWITCH: ALOGI("Dropped event because of pending overdue app switch."); reason = "inbound event was dropped because of pending overdue app switch"; break; - case DROP_REASON_BLOCKED: + case DropReason::BLOCKED: ALOGI("Dropped event because the current application is not responding and the user " "has started interacting with a different application."); reason = "inbound event was dropped because the current application is not responding " "and the user has started interacting with a different application"; break; - case DROP_REASON_STALE: + case DropReason::STALE: ALOGI("Dropped event because it is stale."); reason = "inbound event was dropped because it is stale"; break; - default: - ALOG_ASSERT(false); + case DropReason::NOT_DROPPED: { + LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event"); return; + } } switch (entry->type) { @@ -869,15 +870,15 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { - if (*dropReason == DROP_REASON_NOT_DROPPED) { - *dropReason = DROP_REASON_POLICY; + if (*dropReason == DropReason::NOT_DROPPED) { + *dropReason = DropReason::POLICY; } } // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { + if (*dropReason != DropReason::NOT_DROPPED) { setInjectionResult(entry, - *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); mReporter->reportDroppedKey(entry->sequenceNum); return true; @@ -926,9 +927,9 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent } // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { + if (*dropReason != DropReason::NOT_DROPPED) { setInjectionResult(entry, - *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); return true; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1f906e43f6..9d22e2c38e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -113,14 +113,13 @@ public: virtual status_t pilferPointers(const sp& token) override; private: - - enum DropReason { - DROP_REASON_NOT_DROPPED = 0, - DROP_REASON_POLICY = 1, - DROP_REASON_APP_SWITCH = 2, - DROP_REASON_DISABLED = 3, - DROP_REASON_BLOCKED = 4, - DROP_REASON_STALE = 5, + enum class DropReason { + NOT_DROPPED, + POLICY, + APP_SWITCH, + DISABLED, + BLOCKED, + STALE, }; sp mPolicy; -- cgit v1.2.3-59-g8ed1b From 2574dfa1fe4b83cda231a4f4ec4de3cb5f5109a5 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 16 Oct 2019 16:35:07 -0700 Subject: Make InputReader test components thread safe This changes all assert*() methods in the following classes to enable waiting for the assertion to be true: - FakeInputReaderPolicy - FakeEventHub - FakeInputMapper - TestInputListener This change should make InputReader robust to being run on another thread. Bug: 130819454 Test: atest inputflinger_tests # on crosshatch Change-Id: I9619109e3377eb706d71fb34d525bd3bdc4ef81a --- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/InputReader_test.cpp | 133 ++++++++++++++++------ services/inputflinger/tests/TestInputListener.cpp | 119 ++++++++++++------- services/inputflinger/tests/TestInputListener.h | 37 ++++-- 4 files changed, 205 insertions(+), 85 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index c4f8626a98..ada2266333 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -15,6 +15,7 @@ cc_test { "-Werror", "-Wextra", "-Wno-unused-parameter", + "-Wthread-safety", ], shared_libs: [ "android.hardware.input.classifier@1.0", diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 31b1652806..2b3257d132 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -25,13 +25,18 @@ #include #include +#include #include #include #include - namespace android { +using std::chrono_literals::operator""ms; + +// Timeout for waiting for an expected event +static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; + // An arbitrary time value. static const nsecs_t ARBITRARY_TIME = 1234; @@ -164,9 +169,13 @@ private: // --- FakeInputReaderPolicy --- class FakeInputReaderPolicy : public InputReaderPolicyInterface { + std::mutex mLock; + std::condition_variable mDevicesChangedCondition; + InputReaderConfiguration mConfig; KeyedVector > mPointerControllers; - std::vector mInputDevices; + std::vector mInputDevices GUARDED_BY(mLock); + bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector mViewports; TouchAffineTransformation transform; @@ -177,6 +186,20 @@ public: FakeInputReaderPolicy() { } + void assertInputDevicesChanged() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool devicesChanged = + mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mInputDevicesChanged; + }); + if (!devicesChanged) { + FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; + } + mInputDevicesChanged = false; + } + virtual void clearViewports() { mViewports.clear(); mConfig.setDisplayViewports(mViewports); @@ -291,7 +314,10 @@ private: } virtual void notifyInputDevicesChanged(const std::vector& inputDevices) { + std::scoped_lock lock(mLock); mInputDevices = inputDevices; + mInputDevicesChanged = true; + mDevicesChangedCondition.notify_all(); } virtual sp getKeyboardLayoutOverlay(const InputDeviceIdentifier&) { @@ -342,9 +368,12 @@ class FakeEventHub : public EventHubInterface { } }; + std::mutex mLock; + std::condition_variable mEventsCondition; + KeyedVector mDevices; std::vector mExcludedDevices; - List mEvents; + List mEvents GUARDED_BY(mLock); std::unordered_map> mVideoFrames; public: @@ -496,6 +525,7 @@ public: void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type, int32_t code, int32_t value) { + std::scoped_lock lock(mLock); RawEvent event; event.when = when; event.deviceId = deviceId; @@ -515,8 +545,14 @@ public: } void assertQueueIsEmpty() { - ASSERT_EQ(size_t(0), mEvents.size()) - << "Expected the event queue to be empty (fully consumed)."; + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + const bool queueIsEmpty = + mEventsCondition.wait_for(lock, WAIT_TIMEOUT, + [this]() REQUIRES(mLock) { return mEvents.size() == 0; }); + if (!queueIsEmpty) { + FAIL() << "Timed out waiting for EventHub queue to be emptied."; + } } private: @@ -619,12 +655,14 @@ private: } virtual size_t getEvents(int, RawEvent* buffer, size_t) { + std::scoped_lock lock(mLock); if (mEvents.empty()) { return 0; } *buffer = *mEvents.begin(); mEvents.erase(mEvents.begin()); + mEventsCondition.notify_all(); return 1; } @@ -877,11 +915,13 @@ class FakeInputMapper : public InputMapper { KeyedVector mScanCodeStates; KeyedVector mSwitchStates; std::vector mSupportedKeyCodes; - RawEvent mLastEvent; - bool mConfigureWasCalled; - bool mResetWasCalled; - bool mProcessWasCalled; + std::mutex mLock; + std::condition_variable mStateChangedCondition; + bool mConfigureWasCalled GUARDED_BY(mLock); + bool mResetWasCalled GUARDED_BY(mLock); + bool mProcessWasCalled GUARDED_BY(mLock); + RawEvent mLastEvent GUARDED_BY(mLock); std::optional mViewport; public: @@ -903,20 +943,41 @@ public: } void assertConfigureWasCalled() { - ASSERT_TRUE(mConfigureWasCalled) - << "Expected configure() to have been called."; + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + const bool configureCalled = + mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mConfigureWasCalled; + }); + if (!configureCalled) { + FAIL() << "Expected configure() to have been called."; + } mConfigureWasCalled = false; } void assertResetWasCalled() { - ASSERT_TRUE(mResetWasCalled) - << "Expected reset() to have been called."; + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + const bool resetCalled = + mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mResetWasCalled; + }); + if (!resetCalled) { + FAIL() << "Expected reset() to have been called."; + } mResetWasCalled = false; } void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) { - ASSERT_TRUE(mProcessWasCalled) - << "Expected process() to have been called."; + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + const bool processCalled = + mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mProcessWasCalled; + }); + if (!processCalled) { + FAIL() << "Expected process() to have been called."; + } if (outLastEvent) { *outLastEvent = mLastEvent; } @@ -953,6 +1014,7 @@ private: } virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) { + std::scoped_lock lock(mLock); mConfigureWasCalled = true; // Find the associated viewport if exist. @@ -960,15 +1022,21 @@ private: if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mViewport = config->getDisplayViewportByPort(*displayPort); } + + mStateChangedCondition.notify_all(); } virtual void reset(nsecs_t) { + std::scoped_lock lock(mLock); mResetWasCalled = true; + mStateChangedCondition.notify_all(); } virtual void process(const RawEvent* rawEvent) { + std::scoped_lock lock(mLock); mLastEvent = *rawEvent; mProcessWasCalled = true; + mStateChangedCondition.notify_all(); } virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) { @@ -1033,23 +1101,22 @@ public: } } - void setNextDevice(InputDevice* device) { - mNextDevice = device; - } + void setNextDevice(InputDevice* device) { mNextDevice = device; } InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name, - uint32_t classes, const std::string& location = "") { + uint32_t classes, const std::string& location = "") { InputDeviceIdentifier identifier; identifier.name = name; identifier.location = location; int32_t generation = deviceId + 1; return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier, - classes); + classes); } protected: virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, uint32_t classes) { + const InputDeviceIdentifier& identifier, + uint32_t classes) { if (mNextDevice) { InputDevice* device = mNextDevice; mNextDevice = nullptr; @@ -1281,7 +1348,8 @@ protected: mFakeEventHub->finishDeviceScan(); mReader->loopOnce(); mReader->loopOnce(); - mFakeEventHub->assertQueueIsEmpty(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } void disableDevice(int32_t deviceId, InputDevice* device) { @@ -1316,10 +1384,8 @@ TEST_F(InputReaderTest, GetInputDevices) { ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", 0, nullptr)); // no classes so device will be ignored - std::vector inputDevices; mReader->getInputDevices(inputDevices); - ASSERT_EQ(1U, inputDevices.size()); ASSERT_EQ(1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); @@ -1345,7 +1411,7 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, "fake", deviceClass, nullptr); + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); @@ -1358,20 +1424,20 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { disableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); disableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasNotCalled(); - mFakeListener->assertNotifyConfigurationChangedWasNotCalled(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); enableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); @@ -1529,7 +1595,7 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, "fake", deviceClass, nullptr); + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -1537,19 +1603,19 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { disableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; enableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); mReader->loopOnce(); - mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; } @@ -1577,6 +1643,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 3ee33f1472..06b05acdab 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -19,6 +19,15 @@ #include "TestInputListener.h" +namespace { + +using std::chrono_literals::operator""ms; + +// Timeout for waiting for an expected event +static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms; + +} // namespace + namespace android { // --- TestInputListener --- @@ -29,87 +38,117 @@ TestInputListener::~TestInputListener() { } void TestInputListener::assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs) { - ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty()) - << "Expected notifyConfigurationChanged() to have been called."; - if (outEventArgs) { - *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin(); - } - mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin()); + ASSERT_NO_FATAL_FAILURE( + assertCalled(outEventArgs, + "Expected notifyConfigurationChanged() " + "to have been called.")); } void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() { - ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty()) - << "Expected notifyConfigurationChanged() to not have been called."; + ASSERT_NO_FATAL_FAILURE(assertNotCalled( + "notifyConfigurationChanged() should not be called.")); } void TestInputListener::assertNotifyDeviceResetWasCalled( NotifyDeviceResetArgs* outEventArgs) { - ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty()) - << "Expected notifyDeviceReset() to have been called."; - if (outEventArgs) { - *outEventArgs = *mNotifyDeviceResetArgsQueue.begin(); - } - mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin()); + ASSERT_NO_FATAL_FAILURE( + assertCalled< + NotifyDeviceResetArgs>(outEventArgs, + "Expected notifyDeviceReset() to have been called.")); } void TestInputListener::assertNotifyDeviceResetWasNotCalled() { - ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty()) - << "Expected notifyDeviceReset() to not have been called."; + ASSERT_NO_FATAL_FAILURE( + assertNotCalled("notifyDeviceReset() should not be called.")); } void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) { - ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called."; - if (outEventArgs) { - *outEventArgs = *mNotifyKeyArgsQueue.begin(); - } - mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin()); + ASSERT_NO_FATAL_FAILURE( + assertCalled(outEventArgs, "Expected notifyKey() to have been called.")); } void TestInputListener::assertNotifyKeyWasNotCalled() { - ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called."; + ASSERT_NO_FATAL_FAILURE(assertNotCalled("notifyKey() should not be called.")); } void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) { - ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called."; - if (outEventArgs) { - *outEventArgs = *mNotifyMotionArgsQueue.begin(); - } - mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin()); + ASSERT_NO_FATAL_FAILURE( + assertCalled(outEventArgs, + "Expected notifyMotion() to have been called.")); } void TestInputListener::assertNotifyMotionWasNotCalled() { - ASSERT_TRUE(mNotifyMotionArgsQueue.empty()) - << "Expected notifyMotion() to not have been called."; + ASSERT_NO_FATAL_FAILURE( + assertNotCalled("notifySwitch() should not be called.")); } void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) { - ASSERT_FALSE(mNotifySwitchArgsQueue.empty()) - << "Expected notifySwitch() to have been called."; + ASSERT_NO_FATAL_FAILURE( + assertCalled(outEventArgs, + "Expected notifySwitch() to have been called.")); +} + +template +void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::vector& queue = std::get>(mQueues); + if (queue.empty()) { + const bool eventReceived = + mCondition.wait_for(lock, WAIT_TIMEOUT, + [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + if (!eventReceived) { + FAIL() << "Timed out waiting for event: " << message.c_str(); + } + } if (outEventArgs) { - *outEventArgs = *mNotifySwitchArgsQueue.begin(); + *outEventArgs = *queue.begin(); + } + queue.erase(queue.begin()); +} + +template +void TestInputListener::assertNotCalled(std::string message) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::vector& queue = std::get>(mQueues); + const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); + if (eventReceived) { + FAIL() << "Unexpected event: " << message.c_str(); } - mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin()); +} + +template +void TestInputListener::notify(const NotifyArgsType* args) { + std::scoped_lock lock(mLock); + + std::vector& queue = std::get>(mQueues); + queue.push_back(*args); + mCondition.notify_all(); } void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { - mNotifyConfigurationChangedArgsQueue.push_back(*args); + notify(args); } void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - mNotifyDeviceResetArgsQueue.push_back(*args); + notify(args); } void TestInputListener::notifyKey(const NotifyKeyArgs* args) { - mNotifyKeyArgsQueue.push_back(*args); + notify(args); } void TestInputListener::notifyMotion(const NotifyMotionArgs* args) { - mNotifyMotionArgsQueue.push_back(*args); + notify(args); } void TestInputListener::notifySwitch(const NotifySwitchArgs* args) { - mNotifySwitchArgsQueue.push_back(*args); - } - + notify(args); +} } // namespace android diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 085d3437a0..945e2ea58c 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -17,6 +17,7 @@ #ifndef _UI_TEST_INPUT_LISTENER_H #define _UI_TEST_INPUT_LISTENER_H +#include #include #include "InputListener.h" @@ -25,13 +26,6 @@ namespace android { // --- TestInputListener --- class TestInputListener : public InputListenerInterface { -private: - std::vector mNotifyConfigurationChangedArgsQueue; - std::vector mNotifyDeviceResetArgsQueue; - std::vector mNotifyKeyArgsQueue; - std::vector mNotifyMotionArgsQueue; - std::vector mNotifySwitchArgsQueue; - protected: virtual ~TestInputListener(); @@ -58,15 +52,34 @@ public: void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); private: - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); + template + void assertCalled(NotifyArgsType* outEventArgs, std::string message); + + template + void assertNotCalled(std::string message); + + template + void notify(const NotifyArgsType* args); + + virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + + virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + + virtual void notifyKey(const NotifyKeyArgs* args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); + virtual void notifyMotion(const NotifyMotionArgs* args) override; - virtual void notifyKey(const NotifyKeyArgs* args); + virtual void notifySwitch(const NotifySwitchArgs* args) override; - virtual void notifyMotion(const NotifyMotionArgs* args); + std::mutex mLock; + std::condition_variable mCondition; - virtual void notifySwitch(const NotifySwitchArgs* args); + std::tuple, // + std::vector, // + std::vector, // + std::vector, // + std::vector> // + mQueues GUARDED_BY(mLock); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From d27700495cfd0835bbbec4106942b7294e93d10e Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 29 Oct 2019 11:08:14 -0700 Subject: Use const reference when not modifying objects Before we can refactor to use shared_ptr, we should make ownership clear by only using reference and const reference in places that don't care about the ownership model of the object (and when the object is not nullable). Bug: none Test: none Change-Id: If794e2e1403b31d44524ef88b71c167215fafe46 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 246 ++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 26 +-- services/inputflinger/dispatcher/InputState.cpp | 86 +++---- services/inputflinger/dispatcher/InputState.h | 14 +- 4 files changed, 186 insertions(+), 186 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 90cc5c609f..58404a2510 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -363,7 +363,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Poke user activity for this event. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { - pokeUserActivityLocked(mPendingEvent); + pokeUserActivityLocked(*mPendingEvent); } // Get ready to dispatch the event. @@ -404,14 +404,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast(mPendingEvent); if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(typedEntry)) { + if (isAppSwitchKeyEvent(*typedEntry)) { resetPendingAppSwitchLocked(true); isAppSwitchDue = false; } else if (dropReason == DropReason::NOT_DROPPED) { dropReason = DropReason::APP_SWITCH; } } - if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) { dropReason = DropReason::STALE; } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { @@ -426,7 +426,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { dropReason = DropReason::APP_SWITCH; } - if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) { dropReason = DropReason::STALE; } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { @@ -443,7 +443,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (done) { if (dropReason != DropReason::NOT_DROPPED) { - dropInboundEventLocked(mPendingEvent, dropReason); + dropInboundEventLocked(*mPendingEvent, dropReason); } mLastDropReason = dropReason; @@ -462,16 +462,16 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // the app switch key. - KeyEntry* keyEntry = static_cast(entry); + const KeyEntry& keyEntry = static_cast(*entry); if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { + if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; - } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { if (mAppSwitchSawKeyDown) { #if DEBUG_APP_SWITCH ALOGD("App switch is pending!"); #endif - mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; + mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; mAppSwitchSawKeyDown = false; needWake = true; } @@ -591,7 +591,7 @@ void InputDispatcher::addGestureMonitors(const std::vector& monitors, } } -void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { +void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) { const char* reason; switch (dropReason) { case DropReason::POLICY: @@ -626,15 +626,15 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR } } - switch (entry->type) { + switch (entry.type) { case EventEntry::TYPE_KEY: { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); break; } case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(entry); - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { + const MotionEntry& motionEntry = static_cast(entry); + if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); } else { @@ -651,10 +651,10 @@ static bool isAppSwitchKeyCode(int32_t keyCode) { keyCode == AKEYCODE_APP_SWITCH; } -bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) { - return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) && - (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) && - (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); +bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) { + return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) && + (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) && + (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER); } bool InputDispatcher::isAppSwitchPendingLocked() { @@ -673,8 +673,8 @@ void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { #endif } -bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { - return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT; +bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { + return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } bool InputDispatcher::haveCommandsLocked() const { @@ -837,7 +837,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, entry->dispatchInProgress = true; - logOutboundKeyDetails("dispatchKey - ", entry); + logOutboundKeyDetails("dispatchKey - ", *entry); } // Handle case where the policy asked us to try again later last time. @@ -858,7 +858,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, std::unique_ptr commandEntry = std::make_unique( &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); + getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry)); if (focusedWindowHandle != nullptr) { commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); } @@ -887,7 +887,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Identify targets. std::vector inputTargets; int32_t injectionResult = - findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); + findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } @@ -898,21 +898,21 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, } // Add monitor channels from event's or focused display. - addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); + addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; } -void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) { +void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, - prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, - entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount, entry->downTime); + prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags, + entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, + entry.repeatCount, entry.downTime); #endif } @@ -923,7 +923,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent if (!entry->dispatchInProgress) { entry->dispatchInProgress = true; - logOutboundMotionDetails("dispatchMotion - ", entry); + logOutboundMotionDetails("dispatchMotion - ", *entry); } // Clean up if dropping the event. @@ -944,12 +944,12 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent if (isPointerEvent) { // Pointer event. (eg. touchscreen) injectionResult = - findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, + findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } else { // Non touch event. (eg. trackball) injectionResult = - findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); + findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); } if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; @@ -968,7 +968,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent } // Add monitor channels from event's or focused display. - addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); + addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); if (isPointerEvent) { ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId); @@ -996,33 +996,32 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent return true; } -void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) { +void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", policyFlags=0x%x, " "action=0x%x, actionButton=0x%x, flags=0x%x, " "metaState=0x%x, buttonState=0x%x," "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, - entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState, - entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, - entry->downTime); + prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags, + entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState, + entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime); - for (uint32_t i = 0; i < entry->pointerCount; i++) { + for (uint32_t i = 0; i < entry.pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", - i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType, - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType, + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif } @@ -1036,7 +1035,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true - pokeUserActivityLocked(eventEntry); + pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { sp connection = getConnectionLocked(inputTarget.inputChannel); @@ -1053,7 +1052,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event } int32_t InputDispatcher::handleTargetsNotReadyLocked( - nsecs_t currentTime, const EventEntry* entry, + nsecs_t currentTime, const EventEntry& entry, const sp& applicationHandle, const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { if (applicationHandle == nullptr && windowHandle == nullptr) { @@ -1103,7 +1102,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, + onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, mInputTargetWaitStartTime, reason); // Force poll loop to wake up immediately on next iteration once we get the @@ -1175,21 +1174,21 @@ void InputDispatcher::resetANRTimeoutsLocked() { * then it should be dispatched to that display. Otherwise, the event goes to the focused display. * Focused display is the display that the user most recently interacted with. */ -int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { +int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { int32_t displayId; - switch (entry->type) { + switch (entry.type) { case EventEntry::TYPE_KEY: { - const KeyEntry* typedEntry = static_cast(entry); - displayId = typedEntry->displayId; + const KeyEntry& keyEntry = static_cast(entry); + displayId = keyEntry.displayId; break; } case EventEntry::TYPE_MOTION: { - const MotionEntry* typedEntry = static_cast(entry); - displayId = typedEntry->displayId; + const MotionEntry& motionEntry = static_cast(entry); + displayId = motionEntry.displayId; break; } default: { - ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); + ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry.type); return ADISPLAY_ID_NONE; } } @@ -1197,7 +1196,7 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { } int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, + const EventEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime) { int32_t injectionResult; @@ -1230,7 +1229,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, } // Check permissions. - if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) { + if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; goto Failed; } @@ -1264,7 +1263,7 @@ Unresponsive: } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, + const MotionEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { @@ -1277,8 +1276,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. - int32_t displayId = entry->displayId; - int32_t action = entry->action; + int32_t displayId = entry.displayId; + int32_t action = entry.action; int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; // Update the touch state as needed based on the properties of the touch event. @@ -1298,14 +1297,14 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool isSplit = mTempTouchState.split; bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 && - (mTempTouchState.deviceId != entry->deviceId || - mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId); + (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source || + mTempTouchState.displayId != displayId); bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); - const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; + const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE; bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; @@ -1323,8 +1322,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } mTempTouchState.reset(); mTempTouchState.down = down; - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; + mTempTouchState.deviceId = entry.deviceId; + mTempTouchState.source = entry.source; mTempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { @@ -1348,11 +1347,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, int32_t pointerIndex = getMotionEventActionPointerIndex(action); // Always dispatch mouse events to cursor position. if (isFromMouse) { - x = int32_t(entry->xCursorPosition); - y = int32_t(entry->yCursorPosition); + x = int32_t(entry.xCursorPosition); + y = int32_t(entry.yCursorPosition); } else { - x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); - y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; sp newTouchedWindowHandle = @@ -1410,7 +1409,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Update the temporary touch state. BitSet32 pointerIds; if (isSplit) { - uint32_t pointerId = entry->pointerProperties[pointerIndex].id; + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; pointerIds.markBit(pointerId); } mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); @@ -1432,10 +1431,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Check whether touches should slip outside of the current foreground window. - if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 && + if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && mTempTouchState.isSlippery()) { - int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); sp oldTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); @@ -1469,7 +1468,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, BitSet32 pointerIds; if (isSplit) { - pointerIds.markBit(entry->pointerProperties[0].id); + pointerIds.markBit(entry.pointerProperties[0].id); } mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } @@ -1507,7 +1506,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { haveForegroundWindow = true; - if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) { + if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; goto Failed; @@ -1613,7 +1612,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, Failed: // Check injection permission once and for all. if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { - if (checkInjectionPermission(nullptr, entry->injectionState)) { + if (checkInjectionPermission(nullptr, entry.injectionState)) { injectionPermission = INJECTION_PERMISSION_GRANTED; } else { injectionPermission = INJECTION_PERMISSION_DENIED; @@ -1642,8 +1641,8 @@ Failed: mTempTouchState.reset(); if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; + mTempTouchState.deviceId = entry.deviceId; + mTempTouchState.source = entry.source; mTempTouchState.displayId = displayId; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP || @@ -1662,7 +1661,7 @@ Failed: // One pointer went up. if (isSplit) { int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry->pointerProperties[pointerIndex].id; + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; for (size_t i = 0; i < mTempTouchState.windows.size();) { TouchedWindow& touchedWindow = mTempTouchState.windows[i]; @@ -1822,7 +1821,7 @@ bool InputDispatcher::isWindowObscuredLocked(const sp& window std::string InputDispatcher::checkWindowReadyForMoreInputLocked( nsecs_t currentTime, const sp& windowHandle, - const EventEntry* eventEntry, const char* targetType) { + const EventEntry& eventEntry, const char* targetType) { // If the window is paused then keep waiting. if (windowHandle->getInfo()->paused) { return StringPrintf("Waiting because the %s window is paused.", targetType); @@ -1854,7 +1853,7 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( } // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry->type == EventEntry::TYPE_KEY) { + if (eventEntry.type == EventEntry::TYPE_KEY) { // If the event is a key event, then we must wait for all previous events to // complete before delivering it because previous events may have the // side-effect of transferring focus to a different window and we want to @@ -1926,7 +1925,7 @@ std::string InputDispatcher::getApplicationWindowLabel( } } -void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { +void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { int32_t displayId = getTargetDisplayId(eventEntry); sp focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); @@ -1941,21 +1940,21 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } int32_t eventType = USER_ACTIVITY_EVENT_OTHER; - switch (eventEntry->type) { + switch (eventEntry.type) { case EventEntry::TYPE_MOTION: { - const MotionEntry* motionEntry = static_cast(eventEntry); - if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { + const MotionEntry& motionEntry = static_cast(eventEntry); + if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) { return; } - if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { + if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { eventType = USER_ACTIVITY_EVENT_TOUCH; } break; } case EventEntry::TYPE_KEY: { - const KeyEntry* keyEntry = static_cast(eventEntry); - if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { + const KeyEntry& keyEntry = static_cast(eventEntry); + if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) { return; } eventType = USER_ACTIVITY_EVENT_BUTTON; @@ -1965,7 +1964,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { std::unique_ptr commandEntry = std::make_unique(&InputDispatcher::doPokeUserActivityLockedInterruptible); - commandEntry->eventTime = eventEntry->eventTime; + commandEntry->eventTime = eventEntry.eventTime; commandEntry->userActivityEventType = eventType; postCommandLocked(std::move(commandEntry)); } @@ -2003,8 +2002,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (inputTarget->flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); - MotionEntry* originalMotionEntry = static_cast(eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { + const MotionEntry& originalMotionEntry = static_cast(*eventEntry); + if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); if (!splitMotionEntry) { @@ -2013,7 +2012,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (DEBUG_FOCUS) { ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); - logOutboundMotionDetails(" ", splitMotionEntry); + logOutboundMotionDetails(" ", *splitMotionEntry); } enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); splitMotionEntry->release(); @@ -2086,9 +2085,9 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // Apply target flags and update the connection's input state. switch (eventEntry->type) { case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast(eventEntry); - dispatchEntry->resolvedAction = keyEntry->action; - dispatchEntry->resolvedFlags = keyEntry->flags; + const KeyEntry& keyEntry = static_cast(*eventEntry); + dispatchEntry->resolvedAction = keyEntry.action; + dispatchEntry->resolvedFlags = keyEntry.flags; if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { @@ -2103,7 +2102,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(eventEntry); + const MotionEntry& motionEntry = static_cast(*eventEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2115,11 +2114,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; } else { - dispatchEntry->resolvedAction = motionEntry->action; + dispatchEntry->resolvedAction = motionEntry.action; } if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && - !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source, - motionEntry->displayId)) { + !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source, + motionEntry.displayId)) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter " "event", @@ -2128,7 +2127,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; } - dispatchEntry->resolvedFlags = motionEntry->flags; + dispatchEntry->resolvedFlags = motionEntry.flags; if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; } @@ -2147,7 +2146,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio return; // skip the inconsistent event } - dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction, + dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken()); break; @@ -2509,11 +2508,12 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::TYPE_KEY: logOutboundKeyDetails("cancel - ", - static_cast(cancelationEventEntry)); + static_cast(*cancelationEventEntry)); break; case EventEntry::TYPE_MOTION: logOutboundMotionDetails("cancel - ", - static_cast(cancelationEventEntry)); + static_cast( + *cancelationEventEntry)); break; } @@ -2545,7 +2545,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } } -MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, +MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); @@ -2553,19 +2553,19 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotion PointerProperties splitPointerProperties[MAX_POINTERS]; PointerCoords splitPointerCoords[MAX_POINTERS]; - uint32_t originalPointerCount = originalMotionEntry->pointerCount; + uint32_t originalPointerCount = originalMotionEntry.pointerCount; uint32_t splitPointerCount = 0; for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; originalPointerIndex++) { const PointerProperties& pointerProperties = - originalMotionEntry->pointerProperties[originalPointerIndex]; + originalMotionEntry.pointerProperties[originalPointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); if (pointerIds.hasBit(pointerId)) { splitPointerIndexMap[splitPointerCount] = originalPointerIndex; splitPointerProperties[splitPointerCount].copyFrom(pointerProperties); splitPointerCoords[splitPointerCount].copyFrom( - originalMotionEntry->pointerCoords[originalPointerIndex]); + originalMotionEntry.pointerCoords[originalPointerIndex]); splitPointerCount += 1; } } @@ -2583,13 +2583,13 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotion return nullptr; } - int32_t action = originalMotionEntry->action; + int32_t action = originalMotionEntry.action; int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); const PointerProperties& pointerProperties = - originalMotionEntry->pointerProperties[originalPointerIndex]; + originalMotionEntry.pointerProperties[originalPointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); if (pointerIds.hasBit(pointerId)) { if (pointerIds.count() == 1) { @@ -2613,19 +2613,19 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotion } MotionEntry* splitMotionEntry = - new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, - originalMotionEntry->deviceId, originalMotionEntry->source, - originalMotionEntry->displayId, originalMotionEntry->policyFlags, - action, originalMotionEntry->actionButton, originalMotionEntry->flags, - originalMotionEntry->metaState, originalMotionEntry->buttonState, - originalMotionEntry->classification, originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, - originalMotionEntry->xCursorPosition, - originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, + new MotionEntry(originalMotionEntry.sequenceNum, originalMotionEntry.eventTime, + originalMotionEntry.deviceId, originalMotionEntry.source, + originalMotionEntry.displayId, originalMotionEntry.policyFlags, action, + originalMotionEntry.actionButton, originalMotionEntry.flags, + originalMotionEntry.metaState, originalMotionEntry.buttonState, + originalMotionEntry.classification, originalMotionEntry.edgeFlags, + originalMotionEntry.xPrecision, originalMotionEntry.yPrecision, + originalMotionEntry.xCursorPosition, + originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); - if (originalMotionEntry->injectionState) { - splitMotionEntry->injectionState = originalMotionEntry->injectionState; + if (originalMotionEntry.injectionState) { + splitMotionEntry->injectionState = originalMotionEntry.injectionState; splitMotionEntry->injectionState->refCount += 1; } @@ -4476,7 +4476,7 @@ void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) entry->downTime, entry->eventTime); } -void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, +void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { // TODO Write some statistics about how long we spend waiting. diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 9d22e2c38e..d7d7e17cd7 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -144,7 +144,7 @@ private: bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); // Cleans up input state when dropping an inbound event. - void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); + void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock); // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); @@ -153,12 +153,12 @@ private: bool mAppSwitchSawKeyDown GUARDED_BY(mLock); nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); - bool isAppSwitchKeyEvent(KeyEntry* keyEntry); + bool isAppSwitchKeyEvent(const KeyEntry& keyEntry); bool isAppSwitchPendingLocked() REQUIRES(mLock); void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); // Stale event latency optimization. - static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); + static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); // Blocked event latency optimization. Drops old events when the user intends // to transfer focus to a new application. @@ -293,8 +293,8 @@ private: void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, const std::vector& inputTargets) REQUIRES(mLock); - void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); - void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); + void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); + void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); // Keeping track of ANR timeouts. enum InputTargetWaitCause { @@ -313,7 +313,7 @@ private: sp mLastHoverWindowHandle GUARDED_BY(mLock); // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, + int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry, const sp& applicationHandle, const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) @@ -327,11 +327,11 @@ private: nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); void resetANRTimeoutsLocked() REQUIRES(mLock); - int32_t getTargetDisplayId(const EventEntry* entry); - int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, + int32_t getTargetDisplayId(const EventEntry& entry); + int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock); - int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, + int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); @@ -350,7 +350,7 @@ private: void addGlobalMonitoringTargetsLocked(std::vector& inputTargets, int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); - void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); + void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); bool checkInjectionPermission(const sp& windowHandle, const InjectionState* injectionState); bool isWindowObscuredAtPointLocked(const sp& windowHandle, int32_t x, @@ -361,7 +361,7 @@ private: std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, const sp& windowHandle, - const EventEntry* eventEntry, + const EventEntry& eventEntry, const char* targetType) REQUIRES(mLock); // Manage the dispatch cycle for a single connection. @@ -405,7 +405,7 @@ private: REQUIRES(mLock); // Splitting motion events across windows. - MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); + MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds); // Reset and drop everything the dispatcher is doing. void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); @@ -458,7 +458,7 @@ private: LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; void reportTouchEventForStatistics(const MotionEntry& entry); - void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, + void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const sp& connection); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index c60700e369..603c257c4d 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -36,12 +36,12 @@ bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId return false; } -bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) { +bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) { switch (action) { case AKEY_EVENT_ACTION_UP: { - if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) { for (size_t i = 0; i < mFallbackKeys.size();) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { + if (mFallbackKeys.valueAt(i) == entry.keyCode) { mFallbackKeys.removeItemsAt(i); } else { i += 1; @@ -65,7 +65,7 @@ bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " "keyCode=%d, scanCode=%d", - entry->deviceId, entry->source, entry->keyCode, entry->scanCode); + entry.deviceId, entry.source, entry.keyCode, entry.scanCode); #endif return false; */ @@ -86,7 +86,7 @@ bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) } } -bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) { +bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) { int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; switch (actionMasked) { case AMOTION_EVENT_ACTION_UP: @@ -99,7 +99,7 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " "displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); + entry.deviceId, entry.source, entry.displayId, actionMasked); #endif return false; } @@ -116,7 +116,7 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_MOVE: { - if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { + if (entry.source & AINPUT_SOURCE_CLASS_NAVIGATION) { // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need // to generate cancellation events for these since they're based in relative rather // than absolute units. @@ -125,20 +125,20 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { + if (entry.source & AINPUT_SOURCE_CLASS_JOYSTICK) { // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. // Any other value and we need to track the motion so we can send cancellation // events for anything generating fallback events (e.g. DPad keys for joystick // movements). if (index >= 0) { - if (entry->pointerCoords[0].isEmpty()) { + if (entry.pointerCoords[0].isEmpty()) { mMotionMementos.erase(mMotionMementos.begin() + index); } else { MotionMemento& memento = mMotionMementos[index]; memento.setPointers(entry); } - } else if (!entry->pointerCoords[0].isEmpty()) { + } else if (!entry.pointerCoords[0].isEmpty()) { addMotionMemento(entry, flags, false /*hovering*/); } @@ -153,7 +153,7 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion pointer up/down or move event: " "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); + entry.deviceId, entry.source, entry.displayId, actionMasked); #endif return false; } @@ -167,7 +167,7 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " "displayId=%" PRId32, - entry->deviceId, entry->source, entry->displayId); + entry.deviceId, entry.source, entry.displayId); #endif return false; } @@ -187,65 +187,65 @@ bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t f } } -ssize_t InputState::findKeyMemento(const KeyEntry* entry) const { +ssize_t InputState::findKeyMemento(const KeyEntry& entry) const { for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos[i]; - if (memento.deviceId == entry->deviceId && memento.source == entry->source && - memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && - memento.scanCode == entry->scanCode) { + if (memento.deviceId == entry.deviceId && memento.source == entry.source && + memento.displayId == entry.displayId && memento.keyCode == entry.keyCode && + memento.scanCode == entry.scanCode) { return i; } } return -1; } -ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const { +ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const { for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos[i]; - if (memento.deviceId == entry->deviceId && memento.source == entry->source && - memento.displayId == entry->displayId && memento.hovering == hovering) { + if (memento.deviceId == entry.deviceId && memento.source == entry.source && + memento.displayId == entry.displayId && memento.hovering == hovering) { return i; } } return -1; } -void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { +void InputState::addKeyMemento(const KeyEntry& entry, int32_t flags) { KeyMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.metaState = entry->metaState; + memento.deviceId = entry.deviceId; + memento.source = entry.source; + memento.displayId = entry.displayId; + memento.keyCode = entry.keyCode; + memento.scanCode = entry.scanCode; + memento.metaState = entry.metaState; memento.flags = flags; - memento.downTime = entry->downTime; - memento.policyFlags = entry->policyFlags; + memento.downTime = entry.downTime; + memento.policyFlags = entry.policyFlags; mKeyMementos.push_back(memento); } -void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) { +void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering) { MotionMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; + memento.deviceId = entry.deviceId; + memento.source = entry.source; + memento.displayId = entry.displayId; memento.flags = flags; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.xCursorPosition = entry->xCursorPosition; - memento.yCursorPosition = entry->yCursorPosition; - memento.downTime = entry->downTime; + memento.xPrecision = entry.xPrecision; + memento.yPrecision = entry.yPrecision; + memento.xCursorPosition = entry.xCursorPosition; + memento.yCursorPosition = entry.yCursorPosition; + memento.downTime = entry.downTime; memento.setPointers(entry); memento.hovering = hovering; - memento.policyFlags = entry->policyFlags; + memento.policyFlags = entry.policyFlags; mMotionMementos.push_back(memento); } -void InputState::MotionMemento::setPointers(const MotionEntry* entry) { - pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < entry->pointerCount; i++) { - pointerProperties[i].copyFrom(entry->pointerProperties[i]); - pointerCoords[i].copyFrom(entry->pointerCoords[i]); +void InputState::MotionMemento::setPointers(const MotionEntry& entry) { + pointerCount = entry.pointerCount; + for (uint32_t i = 0; i < entry.pointerCount; i++) { + pointerProperties[i].copyFrom(entry.pointerProperties[i]); + pointerCoords[i].copyFrom(entry.pointerCoords[i]); } } diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 47e9b36219..cc72152d04 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -41,12 +41,12 @@ public: // Records tracking information for a key event that has just been published. // Returns true if the event should be delivered, false if it is inconsistent // and should be skipped. - bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); + bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags); // Records tracking information for a motion event that has just been published. // Returns true if the event should be delivered, false if it is inconsistent // and should be skipped. - bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); + bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags); // Synthesizes cancelation events for the current state and resets the tracked state. void synthesizeCancelationEvents(nsecs_t currentTime, std::vector& outEvents, @@ -100,18 +100,18 @@ private: bool hovering; uint32_t policyFlags; - void setPointers(const MotionEntry* entry); + void setPointers(const MotionEntry& entry); }; std::vector mKeyMementos; std::vector mMotionMementos; KeyedVector mFallbackKeys; - ssize_t findKeyMemento(const KeyEntry* entry) const; - ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + ssize_t findKeyMemento(const KeyEntry& entry) const; + ssize_t findMotionMemento(const MotionEntry& entry, bool hovering) const; - void addKeyMemento(const KeyEntry* entry, int32_t flags); - void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + void addKeyMemento(const KeyEntry& entry, int32_t flags); + void addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering); static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options); -- cgit v1.2.3-59-g8ed1b From 9757f78ee892e2bb08c9db7f407704a6700e71cc Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 29 Oct 2019 12:53:08 -0700 Subject: Return KeyEvent instead of initializing Minor refactor to make ownership of KeyEvent explicit. You will now pass a KeyEntry and receive a KeyEvent in return. Bug: none Test: none Change-Id: I02aaf5f6a2472215b851c9eaadc578aee02ba1cd --- services/inputflinger/dispatcher/InputDispatcher.cpp | 19 +++++++++---------- services/inputflinger/dispatcher/InputDispatcher.h | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 58404a2510..9e5f5f7fb7 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4188,8 +4188,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; - KeyEvent event; - initializeKeyEvent(&event, entry); + KeyEvent event = createKeyEvent(*entry); mLock.unlock(); @@ -4309,8 +4308,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); #endif - KeyEvent event; - initializeKeyEvent(&event, keyEntry); + KeyEvent event = createKeyEvent(*keyEntry); event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); mLock.unlock(); @@ -4352,8 +4350,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); #endif - KeyEvent event; - initializeKeyEvent(&event, keyEntry); + KeyEvent event = createKeyEvent(*keyEntry); mLock.unlock(); @@ -4470,10 +4467,12 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman mLock.lock(); } -void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) { - event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); +KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { + KeyEvent event; + event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags, + entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, + entry.downTime, entry.eventTime); + return event; } void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry, diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d7d7e17cd7..923d0e9ef6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -450,7 +450,7 @@ private: DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock); void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); + KeyEvent createKeyEvent(const KeyEntry& entry); void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Statistics gathering. -- cgit v1.2.3-59-g8ed1b From 00fca7c4db1f24701b17b05321abb9701b695de4 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 29 Oct 2019 13:05:57 -0700 Subject: Return vector of cancellation events Instead of passing a vector to a void function, return the vector from the function to make ownership explicit. In the future, we could return a vector of unique_ptr. Bug: none Test: none Change-Id: I9e50e37fff53fd888149f8686c6671a93e4781b1 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 4 +-- services/inputflinger/dispatcher/InputState.cpp | 39 +++++++++++----------- services/inputflinger/dispatcher/InputState.h | 4 +-- 3 files changed, 24 insertions(+), 23 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9e5f5f7fb7..af671e613a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2493,8 +2493,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( nsecs_t currentTime = now(); - std::vector cancelationEvents; - connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options); + std::vector cancelationEvents = + connection->inputState.synthesizeCancelationEvents(currentTime, options); if (!cancelationEvents.empty()) { #if DEBUG_OUTBOUND_EVENT_DETAILS diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 603c257c4d..c43e304758 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -249,17 +249,17 @@ void InputState::MotionMemento::setPointers(const MotionEntry& entry) { } } -void InputState::synthesizeCancelationEvents(nsecs_t currentTime, - std::vector& outEvents, - const CancelationOptions& options) { +std::vector InputState::synthesizeCancelationEvents( + nsecs_t currentTime, const CancelationOptions& options) { + std::vector events; for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { - outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, - memento.policyFlags, AKEY_EVENT_ACTION_UP, - memento.flags | AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, memento.metaState, - 0, memento.downTime)); + events.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, AKEY_EVENT_ACTION_UP, + memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, + memento.scanCode, memento.metaState, 0 /*repeatCount*/, + memento.downTime)); } } @@ -267,18 +267,19 @@ void InputState::synthesizeCancelationEvents(nsecs_t currentTime, if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back( - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, action, - 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, memento.pointerCount, - memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); + events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, action, 0 /*actionButton*/, + memento.flags, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + memento.pointerCount, memento.pointerProperties, + memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/)); } } + return events; } void InputState::clear() { diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index cc72152d04..a93f486596 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -49,8 +49,8 @@ public: bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags); // Synthesizes cancelation events for the current state and resets the tracked state. - void synthesizeCancelationEvents(nsecs_t currentTime, std::vector& outEvents, - const CancelationOptions& options); + std::vector synthesizeCancelationEvents(nsecs_t currentTime, + const CancelationOptions& options); // Clears the current state. void clear(); -- cgit v1.2.3-59-g8ed1b From d0d71b6e50a7775570905590c93220543c66c943 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 14 Oct 2019 14:50:45 -0700 Subject: Look up Connection by token Currently, we are passing in the entire input channel in order to find the corresponding connection. But if an input channel is uniquely identified by the token, we should only have to pass in the token. In some cases, we are even doing the circular lookup: find input channel by token, then use inputchannel->token to find the corresponding connection. We should be able to unregisterInputChannel by token instead of by inputchannel. Bug: 142581626 Test: basic interaction with the device Change-Id: Ie438c5136186f9dd14df6df08aaabeb35a336f05 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 44 +++++++++------------- services/inputflinger/dispatcher/InputDispatcher.h | 4 +- 2 files changed, 20 insertions(+), 28 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index af671e613a..24b27b9f15 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1038,7 +1038,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - sp connection = getConnectionLocked(inputTarget.inputChannel); + sp connection = getConnectionLocked(inputTarget.inputChannel->getToken()); if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { @@ -1126,7 +1126,7 @@ void InputDispatcher::removeWindowByTokenLocked(const sp& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp& inputChannel) { + nsecs_t newTimeout, const sp& inputConnectionToken) { if (newTimeout > 0) { // Extend the timeout. mInputTargetWaitTimeoutTime = now() + newTimeout; @@ -1135,13 +1135,9 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( mInputTargetWaitTimeoutExpired = true; // Input state will not be realistic. Mark it out of sync. - sp connection = getConnectionLocked(inputChannel); + sp connection = getConnectionLocked(inputConnectionToken); if (connection != nullptr) { - sp token = connection->inputChannel->getToken(); - - if (token != nullptr) { - removeWindowByTokenLocked(token); - } + removeWindowByTokenLocked(inputConnectionToken); if (connection->status == Connection::STATUS_NORMAL) { CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, @@ -1828,8 +1824,7 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( } // If the window's connection is not registered then keep waiting. - sp connection = - getConnectionLocked(getInputChannelLocked(windowHandle->getToken())); + sp connection = getConnectionLocked(windowHandle->getToken()); if (connection == nullptr) { return StringPrintf("Waiting because the %s window's input channel is not " "registered with the input dispatcher. The window may be in the " @@ -2477,7 +2472,7 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp& channel, const CancelationOptions& options) { - sp connection = getConnectionLocked(channel); + sp connection = getConnectionLocked(channel->getToken()); if (connection == nullptr) { return; } @@ -3571,10 +3566,8 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< return false; } - sp fromChannel = getInputChannelLocked(fromToken); - sp toChannel = getInputChannelLocked(toToken); - sp fromConnection = getConnectionLocked(fromChannel); - sp toConnection = getConnectionLocked(toChannel); + sp fromConnection = getConnectionLocked(fromToken); + sp toConnection = getConnectionLocked(toToken); if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.copyPointerStateTo(toConnection->inputState); CancelationOptions @@ -3873,7 +3866,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan { // acquire lock std::scoped_lock _l(mLock); - sp existingConnection = getConnectionLocked(inputChannel); + sp existingConnection = getConnectionLocked(inputChannel->getToken()); if (existingConnection != nullptr) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); @@ -3948,7 +3941,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp& inputCh status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, bool notify) { - sp connection = getConnectionLocked(inputChannel); + sp connection = getConnectionLocked(inputChannel->getToken()); if (connection == nullptr) { ALOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().c_str()); @@ -4056,14 +4049,14 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked(const sp& inputChannel) { - if (inputChannel == nullptr) { +sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) { + if (inputConnectionToken == nullptr) { return nullptr; } for (const auto& pair : mConnectionsByFd) { - sp connection = pair.second; - if (connection->inputChannel->getToken() == inputChannel->getToken()) { + const sp& connection = pair.second; + if (connection->inputChannel->getToken() == inputConnectionToken) { return connection; } } @@ -4171,17 +4164,16 @@ void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* comm } void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { + sp token = + commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr; mLock.unlock(); nsecs_t newTimeout = - mPolicy->notifyANR(commandEntry->inputApplicationHandle, - commandEntry->inputChannel ? commandEntry->inputChannel->getToken() - : nullptr, - commandEntry->reason); + mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 923d0e9ef6..d21b0a1f64 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -183,7 +183,7 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - sp getConnectionLocked(const sp& inputChannel) REQUIRES(mLock); + sp getConnectionLocked(const sp& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -322,7 +322,7 @@ private: void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputChannel) + const sp& inputConnectionToken) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); void resetANRTimeoutsLocked() REQUIRES(mLock); -- cgit v1.2.3-59-g8ed1b From 26d3cfb571a5cc1567f53c4550fbf9ed7a5d501f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 15 Oct 2019 17:02:32 -0700 Subject: Remove InputChannel::setToken The token is now assigned in the constructor. It is read-only. When you open inputchannelpair, the same token is assigned to both server and client side channels. Bug: 142581626 Test: presubmit Change-Id: I603603844b41f478e244b89dcdd1dab7e6260347 --- include/input/InputTransport.h | 47 ++++++++++++++-------- libs/gui/tests/EndToEndNativeInputTest.cpp | 3 +- libs/input/InputTransport.cpp | 47 +++++++++------------- libs/input/tests/InputChannel_test.cpp | 11 +++-- .../inputflinger/dispatcher/InputDispatcher.cpp | 40 +++++++++--------- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 6 files changed, 78 insertions(+), 74 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 1822e4a0db..94d90ad30b 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -167,11 +167,15 @@ protected: virtual ~InputChannel(); public: - static sp create(const std::string& name, android::base::unique_fd fd); + static sp create(const std::string& name, android::base::unique_fd fd, + sp token); - /* Creates a pair of input channels. + /** + * Create a pair of input channels. + * The two returned input channels are equivalent, and are labeled as "server" and "client" + * for convenience. The two input channels share the same token. * - * Returns OK on success. + * Return OK on success. */ static status_t openInputChannelPair(const std::string& name, sp& outServerChannel, sp& outClientChannel); @@ -179,46 +183,57 @@ public: inline std::string getName() const { return mName; } inline int getFd() const { return mFd.get(); } - /* Sends a message to the other endpoint. + /* Send a message to the other endpoint. * * If the channel is full then the message is guaranteed not to have been sent at all. * Try again after the consumer has sent a finished signal indicating that it has * consumed some of the pending messages from the channel. * - * Returns OK on success. - * Returns WOULD_BLOCK if the channel is full. - * Returns DEAD_OBJECT if the channel's peer has been closed. + * Return OK on success. + * Return WOULD_BLOCK if the channel is full. + * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t sendMessage(const InputMessage* msg); - /* Receives a message sent by the other endpoint. + /* Receive a message sent by the other endpoint. * * If there is no message present, try again after poll() indicates that the fd * is readable. * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no message present. - * Returns DEAD_OBJECT if the channel's peer has been closed. + * Return OK on success. + * Return WOULD_BLOCK if there is no message present. + * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t receiveMessage(InputMessage* msg); - /* Returns a new object that has a duplicate of this channel's fd. */ + /* Return a new object that has a duplicate of this channel's fd. */ sp dup() const; status_t write(Parcel& out) const; static sp read(const Parcel& from); - sp getToken() const; - void setToken(const sp& token); + /** + * The connection token is used to identify the input connection, i.e. + * the pair of input channels that were created simultaneously. Input channels + * are always created in pairs, and the token can be used to find the server-side + * input channel from the client-side input channel, and vice versa. + * + * Do not use connection token to check equality of a specific input channel object + * to another, because two different (client and server) input channels will share the + * same connection token. + * + * Return the token that identifies this connection. + */ + sp getConnectionToken() const; private: - InputChannel(const std::string& name, android::base::unique_fd fd); + InputChannel(const std::string& name, android::base::unique_fd fd, sp token); std::string mName; android::base::unique_fd mFd; - sp mToken = nullptr; + sp mToken; }; /* diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 03b9cd75db..8d36ba7b70 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -69,7 +69,6 @@ public: mSurfaceControl = sc; InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - mServerChannel->setToken(new BBinder()); mInputFlinger = getInputFlinger(); mInputFlinger->registerInputChannel(mServerChannel); @@ -165,7 +164,7 @@ private: } void populateInputInfo(int width, int height) { - mInputInfo.token = mServerChannel->getToken(); + mInputInfo.token = mServerChannel->getConnectionToken(); mInputInfo.name = "Test info"; mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index c4f7fe0bf2..a5dd3c0544 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -11,7 +11,7 @@ #define DEBUG_CHANNEL_MESSAGES 0 // Log debug messages whenever InputChannel objects are created/destroyed -#define DEBUG_CHANNEL_LIFECYCLE 0 +static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false; // Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 @@ -225,28 +225,28 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- InputChannel --- -sp InputChannel::create(const std::string& name, android::base::unique_fd fd) { +sp InputChannel::create(const std::string& name, android::base::unique_fd fd, + sp token) { const int result = fcntl(fd, F_SETFL, O_NONBLOCK); if (result != 0) { LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), strerror(errno)); return nullptr; } - return new InputChannel(name, std::move(fd)); + return new InputChannel(name, std::move(fd), token); } -InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd) - : mName(name), mFd(std::move(fd)) { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel constructed: name='%s', fd=%d", - mName.c_str(), fd); -#endif +InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp token) + : mName(name), mFd(std::move(fd)), mToken(token) { + if (DEBUG_CHANNEL_LIFECYCLE) { + ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get()); + } } InputChannel::~InputChannel() { -#if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); -#endif + if (DEBUG_CHANNEL_LIFECYCLE) { + ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); + } } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -267,13 +267,15 @@ status_t InputChannel::openInputChannelPair(const std::string& name, setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + sp token = new BBinder(); + std::string serverChannelName = name + " (server)"; android::base::unique_fd serverFd(sockets[0]); - outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd)); + outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token); std::string clientChannelName = name + " (client)"; android::base::unique_fd clientFd(sockets[1]); - outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd)); + outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token); return OK; } @@ -369,7 +371,7 @@ sp InputChannel::dup() const { getName().c_str()); return nullptr; } - return InputChannel::create(mName, std::move(newFd)); + return InputChannel::create(mName, std::move(newFd), mToken); } status_t InputChannel::write(Parcel& out) const { @@ -396,24 +398,13 @@ sp InputChannel::read(const Parcel& from) { return nullptr; } - sp channel = InputChannel::create(name, std::move(rawFd)); - if (channel != nullptr) { - channel->setToken(token); - } - return channel; + return InputChannel::create(name, std::move(rawFd), token); } -sp InputChannel::getToken() const { +sp InputChannel::getConnectionToken() const { return mToken; } -void InputChannel::setToken(const sp& token) { - if (mToken != nullptr) { - ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str()); - } - mToken = token; -} - // --- InputPublisher --- InputPublisher::InputPublisher(const sp& channel) : diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 7c331e132d..ada275d014 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -46,7 +46,8 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor android::base::unique_fd sendFd(pipe.sendFd); - sp inputChannel = InputChannel::create("channel name", std::move(sendFd)); + sp inputChannel = + InputChannel::create("channel name", std::move(sendFd), new BBinder()); EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; EXPECT_STREQ("channel name", inputChannel->getName().c_str()) @@ -59,13 +60,11 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor TEST_F(InputChannelTest, SetAndGetToken) { Pipe pipe; + sp token = new BBinder(); sp channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd)); - EXPECT_EQ(channel->getToken(), nullptr); + InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - sp token = new BBinder(); - channel->setToken(token); - EXPECT_EQ(token, channel->getToken()); + EXPECT_EQ(token, channel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 24b27b9f15..58a5b3c8d3 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1038,7 +1038,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - sp connection = getConnectionLocked(inputTarget.inputChannel->getToken()); + sp connection = + getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { @@ -2142,7 +2143,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, - inputTarget->inputChannel->getToken()); + inputTarget->inputChannel->getConnectionToken()); break; } @@ -2472,7 +2473,7 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp& channel, const CancelationOptions& options) { - sp connection = getConnectionLocked(channel->getToken()); + sp connection = getConnectionLocked(channel->getConnectionToken()); if (connection == nullptr) { return; } @@ -2514,7 +2515,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget target; sp windowHandle = - getWindowHandleLocked(connection->inputChannel->getToken()); + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; @@ -3866,7 +3867,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan { // acquire lock std::scoped_lock _l(mLock); - sp existingConnection = getConnectionLocked(inputChannel->getToken()); + sp existingConnection = getConnectionLocked(inputChannel->getConnectionToken()); if (existingConnection != nullptr) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); @@ -3877,7 +3878,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock @@ -3897,7 +3898,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan return BAD_VALUE; } - if (inputChannel->getToken() == nullptr) { + if (inputChannel->getConnectionToken() == nullptr) { ALOGW("Attempted to register input monitor without an identifying token."); return BAD_VALUE; } @@ -3906,7 +3907,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan const int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; auto& monitorsByDisplay = isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; @@ -3941,7 +3942,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp& inputCh status_t InputDispatcher::unregisterInputChannelLocked(const sp& inputChannel, bool notify) { - sp connection = getConnectionLocked(inputChannel->getToken()); + sp connection = getConnectionLocked(inputChannel->getConnectionToken()); if (connection == nullptr) { ALOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().c_str()); @@ -3950,7 +3951,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); ALOG_ASSERT(removed); - mInputChannelsByToken.erase(inputChannel->getToken()); + mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { removeMonitorChannelLocked(inputChannel); @@ -4010,7 +4011,7 @@ status_t InputDispatcher::pilferPointers(const sp& token) { TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); std::optional foundDeviceId; for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { - if (touchedMonitor.monitor.inputChannel->getToken() == token) { + if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) { foundDeviceId = state.deviceId; } } @@ -4041,7 +4042,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( for (const auto& it : mGestureMonitorsByDisplay) { const std::vector& monitors = it.second; for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getToken() == token) { + if (monitor.inputChannel->getConnectionToken() == token) { return it.first; } } @@ -4056,7 +4057,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn for (const auto& pair : mConnectionsByFd) { const sp& connection = pair.second; - if (connection->inputChannel->getToken() == inputConnectionToken) { + if (connection->inputChannel->getConnectionToken() == inputConnectionToken) { return connection; } } @@ -4149,7 +4150,7 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry if (connection->status != Connection::STATUS_ZOMBIE) { mLock.unlock(); - mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken()); + mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); mLock.lock(); } @@ -4165,7 +4166,7 @@ void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* comm void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { sp token = - commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr; + commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); nsecs_t newTimeout = @@ -4186,7 +4187,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( android::base::Timer t; sp token = commandEntry->inputChannel != nullptr - ? commandEntry->inputChannel->getToken() + ? commandEntry->inputChannel->getConnectionToken() : nullptr; nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { @@ -4305,7 +4306,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con mLock.unlock(); - mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event, keyEntry->policyFlags, &event); mLock.lock(); @@ -4346,8 +4347,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con mLock.unlock(); - bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, - keyEntry->policyFlags, &event); + bool fallback = + mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), + &event, keyEntry->policyFlags, &event); mLock.lock(); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 7d69854868..b706a749fe 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -470,7 +470,6 @@ public: const sp& dispatcher, const std::string name, int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId), mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) { - mServerChannel->setToken(new BBinder()); mDispatcher->registerInputChannel(mServerChannel); inputApplicationHandle->updateInfo(); @@ -478,7 +477,7 @@ public: } virtual bool updateInfo() { - mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr; + mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr; mInfo.name = mName; mInfo.layoutParamsFlags = mLayoutParamFlags; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -859,7 +858,6 @@ public: FakeMonitorReceiver(const sp& dispatcher, const std::string name, int32_t displayId, bool isGestureMonitor = false) : FakeInputReceiver(dispatcher, name, displayId) { - mServerChannel->setToken(new BBinder()); mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor); } }; -- cgit v1.2.3-59-g8ed1b From 10793a6d83c558ae2149111ffc7a578783b7e745 Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Tue, 5 Nov 2019 02:17:02 +0000 Subject: Refactor InputReader::mDevices collection type Convert InputReader::mDevices from KeyedVector to unordered_map Bug: none Test: none Change-Id: Id9b1fb96345c6e4e7c7a6c6e73552aad55164e4c --- services/inputflinger/reader/InputReader.cpp | 111 ++++++++++----------- services/inputflinger/reader/include/InputReader.h | 4 +- 2 files changed, 55 insertions(+), 60 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index e57604cbe8..1c5adc3a85 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -71,8 +71,8 @@ InputReader::InputReader(std::shared_ptr eventHub, } InputReader::~InputReader() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + delete devicePair.second; } } @@ -179,8 +179,7 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { + if (mDevices.find(deviceId) != mDevices.end()) { ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); return; } @@ -201,7 +200,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { device->getSources()); } - mDevices.add(deviceId, device); + mDevices.insert({deviceId, device}); bumpGenerationLocked(); if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -210,15 +209,14 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - InputDevice* device = nullptr; - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); + InputDevice* device = deviceIt->second; + mDevices.erase(deviceIt); bumpGenerationLocked(); if (device->isIgnored()) { @@ -315,13 +313,13 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controlle void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Discarding event for unknown deviceId %d.", deviceId); return; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (device->isIgnored()) { // ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; @@ -331,8 +329,8 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* } void InputReader::timeoutExpiredLocked(nsecs_t when) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored()) { device->timeoutExpired(when); } @@ -360,8 +358,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); } else { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->configure(now, &mConfig, changes); } } @@ -371,8 +369,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; mGlobalMetaState |= device->getMetaState(); } } @@ -386,8 +384,8 @@ void InputReader::notifyExternalStylusPresenceChanged() { } void InputReader::getExternalStylusDevicesLocked(std::vector& outDevices) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -397,8 +395,8 @@ void InputReader::getExternalStylusDevicesLocked(std::vector& o } void InputReader::dispatchExternalStylusState(const StylusState& state) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->updateExternalStylusState(state); } } @@ -421,8 +419,8 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, i } void InputReader::fadePointerLocked() { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; device->fadePointer(); } } @@ -446,9 +444,8 @@ void InputReader::getInputDevices(std::vector& outInputDevices) void InputReader::getInputDevicesLocked(std::vector& outInputDevices) { outInputDevices.clear(); - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -479,17 +476,16 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result = (device->*getStateFunc)(sourceMask, code); } } } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. @@ -506,13 +502,13 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 } void InputReader::toggleCapsLockState(int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); return; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (device->isIgnored()) { return; } @@ -533,17 +529,16 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM uint8_t* outFlags) { bool result = false; if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); + for (auto& devicePair : mDevices) { + InputDevice* device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } @@ -568,10 +563,9 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; device->vibrate(pattern, patternSize, repeat, token); } } @@ -579,9 +573,9 @@ void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patte void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; device->cancelVibrate(token); } } @@ -589,9 +583,9 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { bool InputReader::isInputDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); + auto deviceIt = mDevices.find(deviceId); + if (deviceIt != mDevices.end()) { + InputDevice* device = deviceIt->second; return device->isEnabled(); } ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); @@ -601,13 +595,13 @@ bool InputReader::isInputDeviceEnabled(int32_t deviceId) { bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { AutoMutex _l(mLock); - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { + auto deviceIt = mDevices.find(deviceId); + if (deviceIt == mDevices.end()) { ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); return false; } - InputDevice* device = mDevices.valueAt(deviceIndex); + InputDevice* device = deviceIt->second; if (!device->isEnabled()) { ALOGW("Ignoring disabled device %s", device->getName().c_str()); return false; @@ -635,8 +629,9 @@ void InputReader::dump(std::string& dump) { dump += "Input Reader State:\n"; - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); + for (const auto& devicePair : mDevices) { + InputDevice* const device = devicePair.second; + device->dump(dump); } dump += INDENT "Configuration:\n"; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 7b4321ea82..557eb3b7c4 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -23,9 +23,9 @@ #include "InputReaderContext.h" #include -#include #include +#include #include namespace android { @@ -131,7 +131,7 @@ private: static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - KeyedVector mDevices; + std::unordered_map mDevices; // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); -- cgit v1.2.3-59-g8ed1b From f15a8aa344946fb274b1db9f546d5fed746cae70 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 5 Nov 2019 01:10:04 +0000 Subject: Reland "Let InputReader handle its own thread" This CL was first landed in Ic732436d4f00a831e317be1f16ac106a11652cff but was reverted due to flaky tests. The flaky tests were caused by races between the instrumented test classes and the InputReader class under test, which now runs in a new thread. In addition to re-landing the change, this CL fixes the flaky tests by changing the tests to eliminate the race condition. - InputReaderTest should send a configuration change request to InputReader every time a device is enabled or disabled, and the test should wait for notifyDeviceReset to be called on the input listener to ensure it was enabled/disabled successfully. Bug: 130819454 Test: atest inputflinger_tests Test: Touch input works on crosshatch Change-Id: I822d3c33384ebdc1bc850a40534e942a27a79ec9 --- services/inputflinger/InputManager.cpp | 9 ++- services/inputflinger/InputManager.h | 11 ++-- services/inputflinger/InputReaderBase.cpp | 14 ----- services/inputflinger/include/InputReaderBase.h | 48 ++++++++-------- services/inputflinger/reader/InputReader.cpp | 47 ++++++++++++++- services/inputflinger/reader/include/InputReader.h | 16 ++++-- services/inputflinger/tests/InputReader_test.cpp | 66 +++++++++------------- 7 files changed, 120 insertions(+), 91 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index e7640dd6af..1043390f84 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -46,7 +46,6 @@ InputManager::~InputManager() { } void InputManager::initialize() { - mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); } @@ -57,9 +56,9 @@ status_t InputManager::start() { return result; } - result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + result = mReader->start(); if (result) { - ALOGE("Could not start InputReader thread due to error %d.", result); + ALOGE("Could not start InputReader due to error %d.", result); mDispatcherThread->requestExit(); return result; @@ -69,9 +68,9 @@ status_t InputManager::start() { } status_t InputManager::stop() { - status_t result = mReaderThread->requestExitAndWait(); + status_t result = mReader->stop(); if (result) { - ALOGW("Could not stop InputReader thread due to error %d.", result); + ALOGW("Could not stop InputReader due to error %d.", result); } result = mDispatcherThread->requestExitAndWait(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 40f66d82f4..2a7ed0ff44 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -43,15 +43,15 @@ class InputDispatcherThread; /* * The input manager is the core of the system event processing. * - * The input manager uses two threads. + * The input manager has two components. * - * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, - * applies policy, and posts messages to a queue managed by the DispatcherThread. + * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies + * policy, and posts messages to a queue managed by the InputDispatcherThread. * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the * queue and asynchronously dispatches them to applications. * - * By design, the InputReaderThread class and InputDispatcherThread class do not share any - * internal state. Moreover, all communication is done one way from the InputReaderThread + * By design, the InputReader class and InputDispatcherThread class do not share any + * internal state. Moreover, all communication is done one way from the InputReader * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. * @@ -102,7 +102,6 @@ public: private: sp mReader; - sp mReaderThread; sp mClassifier; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 0422d8342b..2d6f2c1cc9 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -33,20 +33,6 @@ using android::base::StringPrintf; namespace android { -// --- InputReaderThread --- - -InputReaderThread::InputReaderThread(const sp& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} - -InputReaderThread::~InputReaderThread() { -} - -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); - return true; -} - // --- InputReaderConfiguration --- std::string InputReaderConfiguration::changesToString(uint32_t changes) { diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 5d576b94f3..56c0a7356d 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -19,12 +19,12 @@ #include "PointerControllerInterface.h" +#include #include #include -#include #include #include -#include +#include #include #include @@ -44,7 +44,16 @@ namespace android { -/* Processes raw input events and sends cooked event data to an input listener. */ +// --- InputReaderInterface --- + +/* The interface for the InputReader shared library. + * + * Manages one or more threads that process raw input events and sends cooked event data to an + * input listener. + * + * The implementation must guarantee thread safety for this interface. However, since the input + * listener is NOT thread safe, all calls to the listener must happen from the same thread. + */ class InputReaderInterface : public virtual RefBase { protected: InputReaderInterface() { } @@ -56,18 +65,17 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the reader has not deadlocked. */ + /* Called by the heartbeat to ensures that the reader has not deadlocked. */ virtual void monitor() = 0; /* Returns true if the input device is enabled. */ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0; - /* Runs a single iteration of the processing loop. - * Nominally reads and processes one incoming message from the EventHub. - * - * This method should be called on the input reader thread. - */ - virtual void loopOnce() = 0; + /* Makes the reader start processing events from the kernel. */ + virtual status_t start() = 0; + + /* Makes the reader stop processing any more events. */ + virtual status_t stop() = 0; /* Gets information about all input devices. * @@ -104,17 +112,7 @@ public: virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; }; -/* Reads raw events from the event hub and processes them, endlessly. */ -class InputReaderThread : public Thread { -public: - explicit InputReaderThread(const sp& reader); - virtual ~InputReaderThread(); - -private: - sp mReader; - - virtual bool threadLoop(); -}; +// --- InputReaderConfiguration --- /* * Input reader configuration. @@ -285,6 +283,8 @@ private: std::vector mDisplays; }; +// --- TouchAffineTransformation --- + struct TouchAffineTransformation { float x_scale; float x_ymix; @@ -307,6 +307,8 @@ struct TouchAffineTransformation { void applyTo(float& x, float& y) const; }; +// --- InputReaderPolicyInterface --- + /* * Input reader policy interface. * @@ -316,8 +318,8 @@ struct TouchAffineTransformation { * The actual implementation is partially supported by callbacks into the DVM * via JNI. This interface is also mocked in the unit tests. * - * These methods must NOT re-enter the input reader since they may be called while - * holding the input reader lock. + * These methods will NOT re-enter the input reader interface, so they may be called from + * any method in the input reader interface. */ class InputReaderPolicyInterface : public virtual RefBase { protected: diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index e57604cbe8..5d52bbc91f 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,16 +38,38 @@ #include #include +#include #include #include #include - +#include using android::base::StringPrintf; namespace android { +// --- InputReader::InputReaderThread --- + +/* Thread that reads raw events from the event hub and processes them, endlessly. */ +class InputReader::InputReaderThread : public Thread { +public: + explicit InputReaderThread(InputReader* reader) + : Thread(/* canCallJava */ true), mReader(reader) {} + + ~InputReaderThread() {} + +private: + InputReader* mReader; + + bool threadLoop() override { + mReader->loopOnce(); + return true; + } +}; + +// --- InputReader --- + InputReader::InputReader(std::shared_ptr eventHub, const sp& policy, const sp& listener) @@ -61,6 +83,7 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); + mThread = new InputReaderThread(this); { // acquire lock AutoMutex _l(mLock); @@ -76,6 +99,28 @@ InputReader::~InputReader() { } } +status_t InputReader::start() { + if (mThread->isRunning()) { + return ALREADY_EXISTS; + } + return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY); +} + +status_t InputReader::stop() { + if (!mThread->isRunning()) { + return OK; + } + if (gettid() == mThread->getTid()) { + ALOGE("InputReader can only be stopped from outside of the InputReaderThread!"); + return INVALID_OPERATION; + } + // Directly calling requestExitAndWait() causes the thread to not exit + // if mEventHub is waiting for a long timeout. + mThread->requestExit(); + mEventHub->wake(); + return mThread->requestExitAndWait(); +} + void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 7b4321ea82..3cf4535a03 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -38,12 +38,12 @@ struct StylusState; * that it sends to the input listener. Some functions of the input reader, such as early * event filtering in low power states, are controlled by a separate policy object. * - * The InputReader owns a collection of InputMappers. Most of the work it does happens - * on the input reader thread but the InputReader can receive queries from other system + * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where + * most of the work happens, but the InputReader can receive queries from other system * components running on arbitrary threads. To keep things manageable, the InputReader * uses a single Mutex to guard its state. The Mutex may be held while calling into the * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. + * InputListener. All calls to InputListener must happen from InputReader's thread. */ class InputReader : public InputReaderInterface { public: @@ -55,7 +55,8 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; - virtual void loopOnce() override; + virtual status_t start() override; + virtual status_t stop() override; virtual void getInputDevices(std::vector& outInputDevices) override; @@ -111,6 +112,9 @@ protected: friend class ContextImpl; private: + class InputReaderThread; + sp mThread; + Mutex mLock; Condition mReaderIsAliveCondition; @@ -133,6 +137,10 @@ private: KeyedVector mDevices; + // With each iteration of the loop, InputReader reads and processes one incoming message from + // the EventHub. + void loopOnce(); + // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c1c912214a..8d4ab6afbd 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1133,12 +1133,8 @@ class InputReaderPolicyTest : public testing::Test { protected: sp mFakePolicy; - virtual void SetUp() { - mFakePolicy = new FakeInputReaderPolicy(); - } - virtual void TearDown() { - mFakePolicy.clear(); - } + virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } + virtual void TearDown() override { mFakePolicy.clear(); } }; /** @@ -1321,18 +1317,20 @@ protected: sp mFakeListener; sp mFakePolicy; std::shared_ptr mFakeEventHub; - sp mReader; + std::unique_ptr mReader; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener); + mReader = std::make_unique(mFakeEventHub, mFakePolicy, + mFakeListener); + ASSERT_EQ(OK, mReader->start()); } - virtual void TearDown() { - mReader.clear(); + virtual void TearDown() override { + ASSERT_EQ(OK, mReader->stop()); mFakeListener.clear(); mFakePolicy.clear(); @@ -1346,24 +1344,18 @@ protected: mFakeEventHub->addConfigurationMap(deviceId, configuration); } mFakeEventHub->finishDeviceScan(); - mReader->loopOnce(); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } void disableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->addDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } void enableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->removeDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); - } - - void configureDevice(uint32_t changes, InputDevice* device) { - device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, @@ -1417,28 +1409,22 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); enableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); } @@ -1560,7 +1546,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); } -TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { +TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) { addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1569,13 +1555,12 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange ASSERT_EQ(ARBITRARY_TIME, args.eventTime); } -TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { +TEST_F(InputReaderTest, ForwardsRawEventsToMappers) { FakeInputMapper* mapper = nullptr; ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; @@ -1602,19 +1587,16 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { uint32_t prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; enableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; @@ -1629,7 +1611,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, "fake", deviceClass, nullptr); const uint8_t hdmi1 = 1; @@ -1637,13 +1618,20 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); // Add default and second display. + mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - mReader->loopOnce(); + + // Add the device, and make sure all of the callbacks are triggered. + // The device is added after the input port associations are processed since + // we do not yet support dynamic device-to-display associations. + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); @@ -1652,6 +1640,8 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Can't dispatch event from a disabled device. disableDevice(deviceId, device); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1674,7 +1664,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1688,7 +1678,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1912,7 +1902,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1926,7 +1916,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2589,7 +2579,7 @@ protected: sp mFakePointerController; - virtual void SetUp() { + virtual void SetUp() override { InputMapperTest::SetUp(); mFakePointerController = new FakePointerController(); -- cgit v1.2.3-59-g8ed1b From 6cbc978d76d1c3b5d48f8018dc291bc987d5ff0f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 15 Nov 2019 17:59:25 +0000 Subject: Revert "Reland "Let InputReader handle its own thread"" Reason for revert: flaky tests b/144546498 Bug: 144546498 Change-Id: Id5a4c8dc8e634b23b560dde6843b5a5860305e5f --- services/inputflinger/InputManager.cpp | 9 +-- services/inputflinger/InputManager.h | 11 ++-- services/inputflinger/InputReaderBase.cpp | 14 +++++ services/inputflinger/include/InputReaderBase.h | 48 ++++++++-------- services/inputflinger/reader/InputReader.cpp | 47 +-------------- services/inputflinger/reader/include/InputReader.h | 16 ++---- services/inputflinger/tests/InputReader_test.cpp | 66 +++++++++++++--------- 7 files changed, 91 insertions(+), 120 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 1043390f84..e7640dd6af 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -46,6 +46,7 @@ InputManager::~InputManager() { } void InputManager::initialize() { + mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); } @@ -56,9 +57,9 @@ status_t InputManager::start() { return result; } - result = mReader->start(); + result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { - ALOGE("Could not start InputReader due to error %d.", result); + ALOGE("Could not start InputReader thread due to error %d.", result); mDispatcherThread->requestExit(); return result; @@ -68,9 +69,9 @@ status_t InputManager::start() { } status_t InputManager::stop() { - status_t result = mReader->stop(); + status_t result = mReaderThread->requestExitAndWait(); if (result) { - ALOGW("Could not stop InputReader due to error %d.", result); + ALOGW("Could not stop InputReader thread due to error %d.", result); } result = mDispatcherThread->requestExitAndWait(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 2a7ed0ff44..40f66d82f4 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -43,15 +43,15 @@ class InputDispatcherThread; /* * The input manager is the core of the system event processing. * - * The input manager has two components. + * The input manager uses two threads. * - * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputDispatcherThread. + * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, + * applies policy, and posts messages to a queue managed by the DispatcherThread. * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the * queue and asynchronously dispatches them to applications. * - * By design, the InputReader class and InputDispatcherThread class do not share any - * internal state. Moreover, all communication is done one way from the InputReader + * By design, the InputReaderThread class and InputDispatcherThread class do not share any + * internal state. Moreover, all communication is done one way from the InputReaderThread * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. * @@ -102,6 +102,7 @@ public: private: sp mReader; + sp mReaderThread; sp mClassifier; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 2d6f2c1cc9..0422d8342b 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -33,6 +33,20 @@ using android::base::StringPrintf; namespace android { +// --- InputReaderThread --- + +InputReaderThread::InputReaderThread(const sp& reader) : + Thread(/*canCallJava*/ true), mReader(reader) { +} + +InputReaderThread::~InputReaderThread() { +} + +bool InputReaderThread::threadLoop() { + mReader->loopOnce(); + return true; +} + // --- InputReaderConfiguration --- std::string InputReaderConfiguration::changesToString(uint32_t changes) { diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 56c0a7356d..5d576b94f3 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -19,12 +19,12 @@ #include "PointerControllerInterface.h" -#include #include #include +#include #include #include -#include +#include #include #include @@ -44,16 +44,7 @@ namespace android { -// --- InputReaderInterface --- - -/* The interface for the InputReader shared library. - * - * Manages one or more threads that process raw input events and sends cooked event data to an - * input listener. - * - * The implementation must guarantee thread safety for this interface. However, since the input - * listener is NOT thread safe, all calls to the listener must happen from the same thread. - */ +/* Processes raw input events and sends cooked event data to an input listener. */ class InputReaderInterface : public virtual RefBase { protected: InputReaderInterface() { } @@ -65,17 +56,18 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; - /* Called by the heartbeat to ensures that the reader has not deadlocked. */ + /* Called by the heatbeat to ensures that the reader has not deadlocked. */ virtual void monitor() = 0; /* Returns true if the input device is enabled. */ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0; - /* Makes the reader start processing events from the kernel. */ - virtual status_t start() = 0; - - /* Makes the reader stop processing any more events. */ - virtual status_t stop() = 0; + /* Runs a single iteration of the processing loop. + * Nominally reads and processes one incoming message from the EventHub. + * + * This method should be called on the input reader thread. + */ + virtual void loopOnce() = 0; /* Gets information about all input devices. * @@ -112,7 +104,17 @@ public: virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; }; -// --- InputReaderConfiguration --- +/* Reads raw events from the event hub and processes them, endlessly. */ +class InputReaderThread : public Thread { +public: + explicit InputReaderThread(const sp& reader); + virtual ~InputReaderThread(); + +private: + sp mReader; + + virtual bool threadLoop(); +}; /* * Input reader configuration. @@ -283,8 +285,6 @@ private: std::vector mDisplays; }; -// --- TouchAffineTransformation --- - struct TouchAffineTransformation { float x_scale; float x_ymix; @@ -307,8 +307,6 @@ struct TouchAffineTransformation { void applyTo(float& x, float& y) const; }; -// --- InputReaderPolicyInterface --- - /* * Input reader policy interface. * @@ -318,8 +316,8 @@ struct TouchAffineTransformation { * The actual implementation is partially supported by callbacks into the DVM * via JNI. This interface is also mocked in the unit tests. * - * These methods will NOT re-enter the input reader interface, so they may be called from - * any method in the input reader interface. + * These methods must NOT re-enter the input reader since they may be called while + * holding the input reader lock. */ class InputReaderPolicyInterface : public virtual RefBase { protected: diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 5d52bbc91f..e57604cbe8 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,38 +38,16 @@ #include #include -#include #include #include #include -#include + using android::base::StringPrintf; namespace android { -// --- InputReader::InputReaderThread --- - -/* Thread that reads raw events from the event hub and processes them, endlessly. */ -class InputReader::InputReaderThread : public Thread { -public: - explicit InputReaderThread(InputReader* reader) - : Thread(/* canCallJava */ true), mReader(reader) {} - - ~InputReaderThread() {} - -private: - InputReader* mReader; - - bool threadLoop() override { - mReader->loopOnce(); - return true; - } -}; - -// --- InputReader --- - InputReader::InputReader(std::shared_ptr eventHub, const sp& policy, const sp& listener) @@ -83,7 +61,6 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); - mThread = new InputReaderThread(this); { // acquire lock AutoMutex _l(mLock); @@ -99,28 +76,6 @@ InputReader::~InputReader() { } } -status_t InputReader::start() { - if (mThread->isRunning()) { - return ALREADY_EXISTS; - } - return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY); -} - -status_t InputReader::stop() { - if (!mThread->isRunning()) { - return OK; - } - if (gettid() == mThread->getTid()) { - ALOGE("InputReader can only be stopped from outside of the InputReaderThread!"); - return INVALID_OPERATION; - } - // Directly calling requestExitAndWait() causes the thread to not exit - // if mEventHub is waiting for a long timeout. - mThread->requestExit(); - mEventHub->wake(); - return mThread->requestExitAndWait(); -} - void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 3cf4535a03..7b4321ea82 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -38,12 +38,12 @@ struct StylusState; * that it sends to the input listener. Some functions of the input reader, such as early * event filtering in low power states, are controlled by a separate policy object. * - * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where - * most of the work happens, but the InputReader can receive queries from other system + * The InputReader owns a collection of InputMappers. Most of the work it does happens + * on the input reader thread but the InputReader can receive queries from other system * components running on arbitrary threads. To keep things manageable, the InputReader * uses a single Mutex to guard its state. The Mutex may be held while calling into the * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. All calls to InputListener must happen from InputReader's thread. + * InputListener. */ class InputReader : public InputReaderInterface { public: @@ -55,8 +55,7 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; - virtual status_t start() override; - virtual status_t stop() override; + virtual void loopOnce() override; virtual void getInputDevices(std::vector& outInputDevices) override; @@ -112,9 +111,6 @@ protected: friend class ContextImpl; private: - class InputReaderThread; - sp mThread; - Mutex mLock; Condition mReaderIsAliveCondition; @@ -137,10 +133,6 @@ private: KeyedVector mDevices; - // With each iteration of the loop, InputReader reads and processes one incoming message from - // the EventHub. - void loopOnce(); - // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8d4ab6afbd..c1c912214a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1133,8 +1133,12 @@ class InputReaderPolicyTest : public testing::Test { protected: sp mFakePolicy; - virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } - virtual void TearDown() override { mFakePolicy.clear(); } + virtual void SetUp() { + mFakePolicy = new FakeInputReaderPolicy(); + } + virtual void TearDown() { + mFakePolicy.clear(); + } }; /** @@ -1317,20 +1321,18 @@ protected: sp mFakeListener; sp mFakePolicy; std::shared_ptr mFakeEventHub; - std::unique_ptr mReader; + sp mReader; - virtual void SetUp() override { + virtual void SetUp() { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = std::make_unique(mFakeEventHub, mFakePolicy, - mFakeListener); - ASSERT_EQ(OK, mReader->start()); + mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener); } - virtual void TearDown() override { - ASSERT_EQ(OK, mReader->stop()); + virtual void TearDown() { + mReader.clear(); mFakeListener.clear(); mFakePolicy.clear(); @@ -1344,18 +1346,24 @@ protected: mFakeEventHub->addConfigurationMap(deviceId, configuration); } mFakeEventHub->finishDeviceScan(); + mReader->loopOnce(); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } void disableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->addDisabledDevice(deviceId); - mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); + configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); } void enableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->removeDisabledDevice(deviceId); - mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); + configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); + } + + void configureDevice(uint32_t changes, InputDevice* device) { + device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); } FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, @@ -1409,22 +1417,28 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); enableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); } @@ -1546,7 +1560,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); } -TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) { +TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1555,12 +1569,13 @@ TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) { ASSERT_EQ(ARBITRARY_TIME, args.eventTime); } -TEST_F(InputReaderTest, ForwardsRawEventsToMappers) { +TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { FakeInputMapper* mapper = nullptr; ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; @@ -1587,16 +1602,19 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { uint32_t prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; enableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; @@ -1611,6 +1629,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN); device->addMapper(mapper); mReader->setNextDevice(device); + addDevice(deviceId, "fake", deviceClass, nullptr); const uint8_t hdmi1 = 1; @@ -1618,20 +1637,13 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); // Add default and second display. - mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - - // Add the device, and make sure all of the callbacks are triggered. - // The device is added after the input port associations are processed since - // we do not yet support dynamic device-to-display associations. - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); @@ -1640,8 +1652,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Can't dispatch event from a disabled device. disableDevice(deviceId, device); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1664,7 +1674,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() override { + virtual void SetUp() { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1678,7 +1688,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() override { + virtual void TearDown() { delete mDevice; delete mFakeContext; @@ -1902,7 +1912,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() override { + virtual void SetUp() { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1916,7 +1926,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() override { + virtual void TearDown() { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2579,7 +2589,7 @@ protected: sp mFakePointerController; - virtual void SetUp() override { + virtual void SetUp() { InputMapperTest::SetUp(); mFakePointerController = new FakePointerController(); -- cgit v1.2.3-59-g8ed1b From 8935a80245c3e30c92cd5835809922a0bc84bfed Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 15 Nov 2019 16:41:44 -0800 Subject: Refactor inputfilter tests We are currently storing several variables from notify*args inside the fake policy, in order to check that we received the right values later. This is inconvenient because it's not clear that these variables are specifically for the inputfilter test. Store these in the input event instead. Bug: none Test: atest inputflinger_tests Change-Id: I34dba73eeacf7dd58b10691c6fad3d5206daa780 --- .../inputflinger/tests/InputDispatcher_test.cpp | 64 ++++++++-------------- 1 file changed, 22 insertions(+), 42 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b706a749fe..8c1991ea74 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -50,43 +50,35 @@ protected: public: FakeInputDispatcherPolicy() { - mInputEventFiltered = false; - mTime = -1; - mAction = -1; - mDisplayId = -1; mOnPointerDownToken.clear(); } - void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) { - ASSERT_TRUE(mInputEventFiltered) - << "Expected filterInputEvent() to have been called."; + void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { + ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; + ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY); - ASSERT_EQ(mTime, args->eventTime) - << "Expected time of filtered event was not matched"; - ASSERT_EQ(mAction, args->action) - << "Expected action of filtered event was not matched"; - ASSERT_EQ(mDisplayId, args->displayId) - << "Expected displayId of filtered event was not matched"; + const KeyEvent& keyEvent = static_cast(*mFilteredEvent); + ASSERT_EQ(keyEvent.getEventTime(), args.eventTime); + ASSERT_EQ(keyEvent.getAction(), args.action); + ASSERT_EQ(keyEvent.getDisplayId(), args.displayId); reset(); } - void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) { - ASSERT_TRUE(mInputEventFiltered) - << "Expected filterInputEvent() to have been called."; + void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) { + ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; + ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION); - ASSERT_EQ(mTime, args->eventTime) - << "Expected time of filtered event was not matched"; - ASSERT_EQ(mAction, args->action) - << "Expected action of filtered event was not matched"; - ASSERT_EQ(mDisplayId, args->displayId) - << "Expected displayId of filtered event was not matched"; + const MotionEvent& motionEvent = static_cast(*mFilteredEvent); + ASSERT_EQ(motionEvent.getEventTime(), args.eventTime); + ASSERT_EQ(motionEvent.getAction(), args.action); + ASSERT_EQ(motionEvent.getDisplayId(), args.displayId); reset(); } void assertFilterInputEventWasNotCalled() { - ASSERT_FALSE(mInputEventFiltered) + ASSERT_EQ(nullptr, mFilteredEvent) << "Expected filterInputEvent() to not have been called."; } @@ -97,10 +89,7 @@ public: } private: - bool mInputEventFiltered; - nsecs_t mTime; - int32_t mAction; - int32_t mDisplayId; + std::unique_ptr mFilteredEvent; sp mOnPointerDownToken; virtual void notifyConfigurationChanged(nsecs_t) { @@ -122,26 +111,20 @@ private: *outConfig = mConfig; } - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) { + virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override { switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast(inputEvent); - mTime = keyEvent->getEventTime(); - mAction = keyEvent->getAction(); - mDisplayId = keyEvent->getDisplayId(); + mFilteredEvent = std::make_unique(*keyEvent); break; } case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent* motionEvent = static_cast(inputEvent); - mTime = motionEvent->getEventTime(); - mAction = motionEvent->getAction(); - mDisplayId = motionEvent->getDisplayId(); + mFilteredEvent = std::make_unique(*motionEvent); break; } } - - mInputEventFiltered = true; return true; } @@ -176,10 +159,7 @@ private: } void reset() { - mInputEventFiltered = false; - mTime = -1; - mAction = -1; - mDisplayId = -1; + mFilteredEvent = nullptr; mOnPointerDownToken.clear(); } }; @@ -931,7 +911,7 @@ protected: mDispatcher->notifyMotion(&motionArgs); if (expectToBeFiltered) { - mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs); + mFakePolicy->assertFilterInputEventWasCalled(motionArgs); } else { mFakePolicy->assertFilterInputEventWasNotCalled(); } @@ -946,7 +926,7 @@ protected: mDispatcher->notifyKey(&keyArgs); if (expectToBeFiltered) { - mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs); + mFakePolicy->assertFilterInputEventWasCalled(keyArgs); } else { mFakePolicy->assertFilterInputEventWasNotCalled(); } -- cgit v1.2.3-59-g8ed1b From c5ca85c5a18541a1ba50367df079c0c03eb238cb Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 15 Nov 2019 17:20:00 -0800 Subject: Add check for action in consumeEvent We would like to check that the action received by input tests is correct. Therefore, we should pass the action to the consumeEvent method. But that would make the existing usage of this method cumbersome (too many arguments). Add convenience methods consumeKeyDown and consumeMotionDown to simplify the existing usage. Bug: none Test: atest inputflinger_tests Change-Id: Ie33d952a9025063b4901e32a3d92e1a19c330b72 --- .../inputflinger/tests/InputDispatcher_test.cpp | 70 ++++++++++++---------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 8c1991ea74..2a29e0dbca 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -365,8 +365,8 @@ public: class FakeInputReceiver { public: - void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId, - int32_t expectedFlags = 0) { + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, + int32_t expectedFlags) { uint32_t consumeSeq; InputEvent* event; status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, @@ -379,33 +379,41 @@ public: ASSERT_EQ(expectedEventType, event->getType()) << mName.c_str() << ": event type should match."; - ASSERT_EQ(expectedDisplayId, event->getDisplayId()) - << mName.c_str() << ": event displayId should be the same as expected."; + EXPECT_EQ(expectedDisplayId, event->getDisplayId()); - int32_t flags; switch (expectedEventType) { case AINPUT_EVENT_TYPE_KEY: { - KeyEvent* typedEvent = static_cast(event); - flags = typedEvent->getFlags(); + const KeyEvent& keyEvent = static_cast(*event); + EXPECT_EQ(expectedAction, keyEvent.getAction()); + EXPECT_EQ(expectedFlags, keyEvent.getFlags()); break; } case AINPUT_EVENT_TYPE_MOTION: { - MotionEvent* typedEvent = static_cast(event); - flags = typedEvent->getFlags(); + const MotionEvent& motionEvent = static_cast(*event); + EXPECT_EQ(expectedAction, motionEvent.getAction()); + EXPECT_EQ(expectedFlags, motionEvent.getFlags()); break; } default: { FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; } } - ASSERT_EQ(expectedFlags, flags) - << mName.c_str() << ": event flags should be the same as expected."; status = mConsumer->sendFinishedSignal(consumeSeq, handled()); ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, + expectedFlags); + } + + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, + expectedFlags); + } + void assertNoEvents() { uint32_t consumeSeq; InputEvent* event; @@ -611,7 +619,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; // Window should receive motion event. - window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); } // The foreground window should receive the first touch down event. @@ -632,7 +640,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; // Top window should receive the touch down event. Second window should not receive anything. - windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowSecond->assertNoEvents(); } @@ -658,7 +666,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Focused window should receive event. windowTop->assertNoEvents(); - windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); } TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { @@ -683,7 +691,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; // Top focused window should receive event. - windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + windowTop->consumeKeyDown(ADISPLAY_ID_NONE); windowSecond->assertNoEvents(); } @@ -713,7 +721,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { // Top window is invalid, so it should not receive any input event. windowTop->assertNoEvents(); - windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); } TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { @@ -738,7 +746,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); - windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowRight->assertNoEvents(); } @@ -794,7 +802,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); // Test touch down on second display. @@ -802,29 +810,29 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); } TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) { // Test inject a key down with display id specified. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT); + windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); // Test inject a key down without display id specified. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); // Remove secondary display. std::vector> noWindows; mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID); // Expect old focus should receive a cancel event. - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE, - AKEY_EVENT_FLAG_CANCELED); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, + AKEY_EVENT_FLAG_CANCELED); // Test inject a key down, should timeout because of no target window. ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) @@ -853,8 +861,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); - monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); monitorInSecondary->assertNoEvents(); @@ -864,8 +872,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary->assertNoEvents(); - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); - monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); + monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); // Test inject a non-pointer motion event. // If specific a display, it will dispatch to the focused window of particular display, @@ -875,8 +883,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary->assertNoEvents(); - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); - monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); + windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE); + monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE); } // Test per-display input monitors for key event. @@ -892,8 +900,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary->assertNoEvents(); - windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); - monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); + monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); } class InputFilterTest : public InputDispatcherTest { -- cgit v1.2.3-59-g8ed1b From 08b574f90cc9a21d5f9d47ec7b1b29a8692553e6 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 15 Nov 2019 18:05:52 -0800 Subject: Add busy wait for consume() call We have no guarantee that consume() will find an available event right away. If we send an event to InputDispatcher, it may take some time for dispatcher to process the event. Only when the event is processed can we expect that it will be written to the socket. In the test, we currently allow no time for this to complete. For the upcoming tests, the work performed by dispatcher might be too large, so the tests would start to fail. If there is no event available at the socket, WOULD_BLOCK would be returned. So if we are sure an event is coming, we can busily wait, as long as the consume call is returning WOULD_BLOCK. Bug: none Test: atest inputflinger_tests The change was tested with a child CL for notifyDeviceReset. Change-Id: Ib9557c4de03c1b1d4a4eb1573d527f47facbf249 --- .../inputflinger/tests/InputDispatcher_test.cpp | 60 +++++++++++++++------- 1 file changed, 42 insertions(+), 18 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2a29e0dbca..8863ec2517 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -365,17 +365,48 @@ public: class FakeInputReceiver { public: - void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, - int32_t expectedFlags) { + InputEvent* consume() { uint32_t consumeSeq; InputEvent* event; - status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, - &consumeSeq, &event); - ASSERT_EQ(OK, status) - << mName.c_str() << ": consumer consume should return OK."; - ASSERT_TRUE(event != nullptr) - << mName.c_str() << ": consumer should have returned non-NULL event."; + std::chrono::time_point start = std::chrono::steady_clock::now(); + status_t status = WOULD_BLOCK; + while (status == WOULD_BLOCK) { + status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq, + &event); + std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; + if (elapsed > 100ms) { + break; + } + } + + if (status == WOULD_BLOCK) { + // Just means there's no event available. + return nullptr; + } + + if (status != OK) { + ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; + return nullptr; + } + if (event == nullptr) { + ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; + return nullptr; + } + + status = mConsumer->sendFinishedSignal(consumeSeq, handled()); + if (status != OK) { + ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + } + return event; + } + + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, + int32_t expectedFlags) { + InputEvent* event = consume(); + + ASSERT_NE(nullptr, event) << mName.c_str() + << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) << mName.c_str() << ": event type should match."; @@ -398,10 +429,6 @@ public: FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; } } - - status = mConsumer->sendFinishedSignal(consumeSeq, handled()); - ASSERT_EQ(OK, status) - << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { @@ -415,13 +442,10 @@ public: } void assertNoEvents() { - uint32_t consumeSeq; - InputEvent* event; - status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, - &consumeSeq, &event); - ASSERT_NE(OK, status) + InputEvent* event = consume(); + ASSERT_EQ(nullptr, event) << mName.c_str() - << ": should not have received any events, so consume(..) should not return OK."; + << ": should not have received any events, so consume() should return NULL"; } protected: -- cgit v1.2.3-59-g8ed1b From 49483274051a25e6fde2a995dc2aee048be02dac Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 22 Oct 2019 13:13:47 -0700 Subject: Use enum class for EventEntry Switch EventEntry to use enum class. This improves type safety and catches missing switch/case statements at compile time. Bug: 70668286 Test: presubmit Change-Id: Ib5f52cb357c87fcac8c7045ae13632814ab518b3 --- services/inputflinger/dispatcher/Entry.cpp | 10 +-- services/inputflinger/dispatcher/Entry.h | 19 ++++- .../inputflinger/dispatcher/InputDispatcher.cpp | 83 ++++++++++++++-------- .../reader/mapper/SwitchInputMapper.cpp | 4 +- 4 files changed, 78 insertions(+), 38 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 930c7c77e7..e925f5b6ba 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -60,7 +60,7 @@ static std::string keyActionToString(int32_t action) { // --- EventEntry --- -EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags) +EventEntry::EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags) : sequenceNum(sequenceNum), refCount(1), type(type), @@ -92,7 +92,7 @@ void EventEntry::releaseInjectionState() { // --- ConfigurationChangedEntry --- ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime) - : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {} + : EventEntry(sequenceNum, Type::CONFIGURATION_CHANGED, eventTime, 0) {} ConfigurationChangedEntry::~ConfigurationChangedEntry() {} @@ -103,7 +103,7 @@ void ConfigurationChangedEntry::appendDescription(std::string& msg) const { // --- DeviceResetEntry --- DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) - : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} + : EventEntry(sequenceNum, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} DeviceResetEntry::~DeviceResetEntry() {} @@ -117,7 +117,7 @@ KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, ui int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime) - : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), + : EventEntry(sequenceNum, Type::KEY, eventTime, policyFlags), deviceId(deviceId), source(source), displayId(displayId), @@ -165,7 +165,7 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset) - : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + : EventEntry(sequenceNum, Type::MOTION, eventTime, policyFlags), eventTime(eventTime), deviceId(deviceId), source(source), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 28c2799477..9dcaadc6ee 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -33,11 +33,24 @@ namespace android::inputdispatcher { constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; struct EventEntry { - enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; + enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION }; + + static const char* typeToString(Type type) { + switch (type) { + case Type::CONFIGURATION_CHANGED: + return "CONFIGURATION_CHANGED"; + case Type::DEVICE_RESET: + return "DEVICE_RESET"; + case Type::KEY: + return "KEY"; + case Type::MOTION: + return "MOTION"; + } + } uint32_t sequenceNum; mutable int32_t refCount; - int32_t type; + Type type; nsecs_t eventTime; uint32_t policyFlags; InjectionState* injectionState; @@ -66,7 +79,7 @@ struct EventEntry { virtual void appendDescription(std::string& msg) const = 0; protected: - EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); + EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); void releaseInjectionState(); }; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 58a5b3c8d3..c219941129 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -386,7 +386,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } switch (mPendingEvent->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { + case EventEntry::Type::CONFIGURATION_CHANGED: { ConfigurationChangedEntry* typedEntry = static_cast(mPendingEvent); done = dispatchConfigurationChangedLocked(currentTime, typedEntry); @@ -394,14 +394,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } - case EventEntry::TYPE_DEVICE_RESET: { + case EventEntry::Type::DEVICE_RESET: { DeviceResetEntry* typedEntry = static_cast(mPendingEvent); done = dispatchDeviceResetLocked(currentTime, typedEntry); dropReason = DropReason::NOT_DROPPED; // device resets are never dropped break; } - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { KeyEntry* typedEntry = static_cast(mPendingEvent); if (isAppSwitchDue) { if (isAppSwitchKeyEvent(*typedEntry)) { @@ -421,7 +421,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { MotionEntry* typedEntry = static_cast(mPendingEvent); if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { dropReason = DropReason::APP_SWITCH; @@ -435,10 +435,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } - - default: - ALOG_ASSERT(false); - break; } if (done) { @@ -458,7 +454,7 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { traceInboundQueueLengthLocked(); switch (entry->type) { - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // the app switch key. @@ -480,7 +476,7 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { // Optimize case where the current application is unresponsive and the user // decides to touch a window in a different application. // If the application takes too long to catch up then we drop all events preceding @@ -508,6 +504,11 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { } break; } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + // nothing to do + break; + } } return needWake; @@ -627,12 +628,12 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } switch (entry.type) { - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(entry); if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); @@ -643,6 +644,11 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } break; } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type)); + break; + } } } @@ -1174,18 +1180,19 @@ void InputDispatcher::resetANRTimeoutsLocked() { int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { int32_t displayId; switch (entry.type) { - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast(entry); displayId = keyEntry.displayId; break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(entry); displayId = motionEntry.displayId; break; } - default: { - ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry.type); + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type)); return ADISPLAY_ID_NONE; } } @@ -1849,7 +1856,7 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( } // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry.type == EventEntry::TYPE_KEY) { + if (eventEntry.type == EventEntry::Type::KEY) { // If the event is a key event, then we must wait for all previous events to // complete before delivering it because previous events may have the // side-effect of transferring focus to a different window and we want to @@ -1937,7 +1944,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry.type) { - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(eventEntry); if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) { return; @@ -1948,7 +1955,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } break; } - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast(eventEntry); if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) { return; @@ -1956,6 +1963,12 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { eventType = USER_ACTIVITY_EVENT_BUTTON; break; } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("%s events are not user activity", + EventEntry::typeToString(eventEntry.type)); + break; + } } std::unique_ptr commandEntry = @@ -1996,7 +2009,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // Split a motion event if needed. if (inputTarget->flags & InputTarget::FLAG_SPLIT) { - ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); + ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { @@ -2080,7 +2093,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // Apply target flags and update the connection's input state. switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast(*eventEntry); dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2097,7 +2110,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(*eventEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; @@ -2147,6 +2160,12 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio break; } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("%s events should not go to apps", + EventEntry::typeToString(eventEntry->type)); + break; + } } // Remember that we are waiting for this dispatch to complete. @@ -2206,7 +2225,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status_t status; EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { + case EventEntry::Type::KEY: { KeyEntry* keyEntry = static_cast(eventEntry); // Publish the key event. @@ -2221,7 +2240,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, break; } - case EventEntry::TYPE_MOTION: { + case EventEntry::Type::MOTION: { MotionEntry* motionEntry = static_cast(eventEntry); PointerCoords scaledCoords[MAX_POINTERS]; @@ -2502,15 +2521,23 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( for (size_t i = 0; i < cancelationEvents.size(); i++) { EventEntry* cancelationEventEntry = cancelationEvents[i]; switch (cancelationEventEntry->type) { - case EventEntry::TYPE_KEY: + case EventEntry::Type::KEY: { logOutboundKeyDetails("cancel - ", static_cast(*cancelationEventEntry)); break; - case EventEntry::TYPE_MOTION: + } + case EventEntry::Type::MOTION: { logOutboundMotionDetails("cancel - ", static_cast( *cancelationEventEntry)); break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", + EventEntry::typeToString(cancelationEventEntry->type)); + break; + } } InputTarget target; @@ -4237,11 +4264,11 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c } bool restartEvent; - if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { + if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); restartEvent = afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { + } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, handled); diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 4ff941f5cf..16095b9eb1 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -56,8 +56,8 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { void SwitchInputMapper::sync(nsecs_t when) { if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, - mUpdatedSwitchMask); + NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0 /*policyFlags*/, + updatedSwitchValues, mUpdatedSwitchMask); getListener()->notifySwitch(&args); mUpdatedSwitchMask = 0; -- cgit v1.2.3-59-g8ed1b From 3b37f9a0b60464ec89b530e6cebc2c3236d0cebc Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Sat, 23 Nov 2019 13:42:41 -0800 Subject: Add toString and remove default case For a future CL, we will need to print some bools. To facilitate that, add a "toString" function. Also, if we add a focus type, it would be good that the compiler highlights cases where the new type isn't being handled in a switch statement. Therefore, remove all default cases in switch statements where feasible. Because we are now adding an "unused" function that compiler complains about, also convert DEBUG_TRANSPORT_ACTIONS into a constexpr bool. Bug: none Test: presubmit Change-Id: Ie8472707f1699d2009232b9f36abae9a36a97f5f --- libs/input/InputTransport.cpp | 147 ++++++++++----------- .../inputflinger/dispatcher/InputDispatcher.cpp | 8 +- 2 files changed, 78 insertions(+), 77 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index a5dd3c0544..8d1dd639bb 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -14,7 +14,7 @@ static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false; // Log debug messages about transport actions -#define DEBUG_TRANSPORT_ACTIONS 0 +static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; // Log debug messages about touch event resampling #define DEBUG_RESAMPLING 0 @@ -88,6 +88,10 @@ inline static bool isPointerEvent(int32_t source) { return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; } +inline static const char* toString(bool value) { + return value ? "true" : "false"; +} + // --- InputMessage --- bool InputMessage::isValid(size_t actualSize) const { @@ -216,10 +220,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.finished.handled = body.finished.handled; break; } - default: { - LOG_FATAL("Unexpected message type %i", header.type); - break; - } } } @@ -432,14 +432,13 @@ status_t InputPublisher::publishKeyEvent( mChannel->getName().c_str(), keyCode); ATRACE_NAME(message.c_str()); } -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " - "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," - "downTime=%" PRId64 ", eventTime=%" PRId64, - mChannel->getName().c_str(), seq, - deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, - downTime, eventTime); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " + "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," + "downTime=%" PRId64 ", eventTime=%" PRId64, + mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode, + metaState, repeatCount, downTime, eventTime); + } if (!seq) { ALOGE("Attempted to publish a key event with sequence number 0."); @@ -476,18 +475,18 @@ status_t InputPublisher::publishMotionEvent( mChannel->getName().c_str(), action); ATRACE_NAME(message.c_str()); } -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " - "displayId=%" PRId32 ", " - "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " - "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32, - mChannel->getName().c_str(), seq, - deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, - buttonState, motionClassificationToString(classification), - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", " + "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " + "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " + "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " + "pointerCount=%" PRIu32, + mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, + flags, edgeFlags, metaState, buttonState, + motionClassificationToString(classification), xOffset, yOffset, xPrecision, + yPrecision, downTime, eventTime, pointerCount); + } if (!seq) { ALOGE("Attempted to publish a motion event with sequence number 0."); @@ -531,10 +530,9 @@ status_t InputPublisher::publishMotionEvent( } status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ receiveFinishedSignal", - mChannel->getName().c_str()); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); + } InputMessage msg; status_t result = mChannel->receiveMessage(&msg); @@ -569,10 +567,10 @@ bool InputConsumer::isTouchResamplingEnabled() { status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, - mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, + mChannel->getName().c_str(), toString(consumeBatches), frameTime); + } *outSeq = 0; *outEvent = nullptr; @@ -592,10 +590,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, if (consumeBatches || result != WOULD_BLOCK) { result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().c_str(), *outSeq); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } break; } } @@ -611,10 +609,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, initializeKeyEvent(keyEvent, &mMsg); *outSeq = mMsg.body.key.seq; *outEvent = keyEvent; -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", - mChannel->getName().c_str(), *outSeq); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } break; } @@ -624,10 +622,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, Batch& batch = mBatches.editItemAt(batchIndex); if (canAddSample(batch, &mMsg)) { batch.samples.push(mMsg); -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ appended to batch event", - mChannel->getName().c_str()); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ appended to batch event", + mChannel->getName().c_str()); + } break; } else if (isPointerEvent(mMsg.body.motion.source) && mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { @@ -649,27 +647,27 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, if (result) { return result; } -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().c_str(), *outSeq); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } break; } } - // Start a new batch if needed. - if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE - || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mBatches.push(); - Batch& batch = mBatches.editTop(); - batch.samples.push(mMsg); -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ started batch event", - mChannel->getName().c_str()); -#endif - break; - } + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || + mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mBatches.push(); + Batch& batch = mBatches.editTop(); + batch.samples.push(mMsg); + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ started batch event", + mChannel->getName().c_str()); + } + break; + } MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; @@ -679,17 +677,18 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } break; } - default: - ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", - mChannel->getName().c_str(), mMsg.header.type); - return UNKNOWN_ERROR; + case InputMessage::Type::FINISHED: { + LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by " + "InputConsumer!"); + break; + } } } return OK; @@ -1014,10 +1013,10 @@ bool InputConsumer::shouldResampleTool(int32_t toolType) { } status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", - mChannel->getName().c_str(), seq, handled ? "true" : "false"); -#endif + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().c_str(), seq, toString(handled)); + } if (!seq) { ALOGE("Attempted to send a finished signal with sequence number 0."); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index c219941129..aba5a30206 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2295,10 +2295,12 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, reportTouchEventForStatistics(*motionEntry); break; } - - default: - ALOG_ASSERT(false); + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", + EventEntry::typeToString(eventEntry->type)); return; + } } // Check the result. -- cgit v1.2.3-59-g8ed1b From d99e1b6ec9b8623ce017d350ee7c31179fb866d9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 Nov 2019 11:01:06 -0800 Subject: Refactor filterInputEvent asserts There are some commonalities between the asserts for filterInputEvent. Refactor them. Bug: none Test: presubmit Change-Id: I19c84ab5df4c347b02b4fcef1045c1e34fcad0f0 --- .../inputflinger/tests/InputDispatcher_test.cpp | 50 +++++++++++----------- 1 file changed, 26 insertions(+), 24 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 8863ec2517..47397795ec 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -54,33 +54,16 @@ public: } void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { - ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; - ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY); - - const KeyEvent& keyEvent = static_cast(*mFilteredEvent); - ASSERT_EQ(keyEvent.getEventTime(), args.eventTime); - ASSERT_EQ(keyEvent.getAction(), args.action); - ASSERT_EQ(keyEvent.getDisplayId(), args.displayId); - - reset(); + assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action, + args.displayId); } void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) { - ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; - ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION); - - const MotionEvent& motionEvent = static_cast(*mFilteredEvent); - ASSERT_EQ(motionEvent.getEventTime(), args.eventTime); - ASSERT_EQ(motionEvent.getAction(), args.action); - ASSERT_EQ(motionEvent.getDisplayId(), args.displayId); - - reset(); + assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action, + args.displayId); } - void assertFilterInputEventWasNotCalled() { - ASSERT_EQ(nullptr, mFilteredEvent) - << "Expected filterInputEvent() to not have been called."; - } + void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); } void assertOnPointerDownEquals(const sp& touchedToken) { ASSERT_EQ(mOnPointerDownToken, touchedToken) @@ -158,10 +141,29 @@ private: mOnPointerDownToken = newToken; } - void reset() { + void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action, + int32_t displayId) { + ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; + ASSERT_EQ(mFilteredEvent->getType(), type); + + if (type == AINPUT_EVENT_TYPE_KEY) { + const KeyEvent& keyEvent = static_cast(*mFilteredEvent); + EXPECT_EQ(keyEvent.getEventTime(), eventTime); + EXPECT_EQ(keyEvent.getAction(), action); + EXPECT_EQ(keyEvent.getDisplayId(), displayId); + } else if (type == AINPUT_EVENT_TYPE_MOTION) { + const MotionEvent& motionEvent = static_cast(*mFilteredEvent); + EXPECT_EQ(motionEvent.getEventTime(), eventTime); + EXPECT_EQ(motionEvent.getAction(), action); + EXPECT_EQ(motionEvent.getDisplayId(), displayId); + } else { + FAIL() << "Unknown type: " << type; + } + mFilteredEvent = nullptr; - mOnPointerDownToken.clear(); } + + void reset() { mOnPointerDownToken.clear(); } }; -- cgit v1.2.3-59-g8ed1b From b9b15358ebdbe8328b7440dd559d575919ad6bf6 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 Nov 2019 13:19:26 -0800 Subject: Rename some variables in InputDispatcher_test Rename variables from mWindowFocused -> mFocusedWindow. Also, remove some of the vector definitions for brevity. Bug: none Test: atest inputflinger_tests Change-Id: Iac34e9bccef4bbdb03c535f92e090b4438e80d64 --- .../inputflinger/tests/InputDispatcher_test.cpp | 72 ++++++++-------------- 1 file changed, 24 insertions(+), 48 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 47397795ec..ebce47f1e7 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -636,10 +636,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(window); - - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -656,11 +653,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { sp windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(windowTop); - inputWindowHandles.push_back(windowSecond); - - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -682,11 +675,8 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Expect one focus window exist in display. windowSecond->setFocus(); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(windowTop); - inputWindowHandles.push_back(windowSecond); - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -708,11 +698,8 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first) windowTop->setFocus(); windowSecond->setFocus(); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(windowTop); - inputWindowHandles.push_back(windowSecond); - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -734,12 +721,9 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowTop->setFocus(); windowSecond->setFocus(); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(windowTop); - inputWindowHandles.push_back(windowSecond); // Release channel for window is no longer valid. windowTop->releaseChannel(); - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); // Test inject a key down, should dispatch to a valid window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -764,8 +748,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - std::vector> inputWindowHandles{windowLeft, windowRight}; - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT); // Inject an event with coordinate in the area of right window, with mouse cursor in the area of // left window. This event should be dispatched to the left window. @@ -786,25 +769,22 @@ public: application1 = new FakeApplicationHandle(); windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT); - std::vector> inputWindowHandles; - inputWindowHandles.push_back(windowInPrimary); + // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); windowInPrimary->setFocus(); - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); application2 = new FakeApplicationHandle(); windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID); // Set focus to second display window. - std::vector> inputWindowHandles_Second; - inputWindowHandles_Second.push_back(windowInSecondary); // Set focus display to second one. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocus(); - mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID); + mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); } virtual void TearDown() { @@ -852,9 +832,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) windowInPrimary->assertNoEvents(); windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); - // Remove secondary display. - std::vector> noWindows; - mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID); + // Remove all windows in secondary display. + mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID); // Expect old focus should receive a cancel event. windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, @@ -1014,34 +993,31 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { // window. mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); - mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second", - ADISPLAY_ID_DEFAULT); - mWindowFocused->setFrame(Rect(50, 50, 100, 100)); - mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); - mWindowFocusedTouchPoint = 60; + mFocusedWindow = + new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); + mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + mFocusedWindowTouchPoint = 60; // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mWindowFocused->setFocus(); + mFocusedWindow->setFocus(); // Expect one focus window exist in display. - std::vector> inputWindowHandles; - inputWindowHandles.push_back(mUnfocusedWindow); - inputWindowHandles.push_back(mWindowFocused); - mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); } virtual void TearDown() { InputDispatcherTest::TearDown(); mUnfocusedWindow.clear(); - mWindowFocused.clear(); + mFocusedWindow.clear(); } protected: sp mUnfocusedWindow; - sp mWindowFocused; - int32_t mWindowFocusedTouchPoint; + sp mFocusedWindow; + int32_t mFocusedWindowTouchPoint; }; // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action @@ -1086,9 +1062,9 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMo // onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint, - mWindowFocusedTouchPoint)) + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + mFocusedWindowTouchPoint, mFocusedWindowTouchPoint)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; // Call monitor to wait for the command queue to get flushed. mDispatcher->monitor(); -- cgit v1.2.3-59-g8ed1b From 240f8313d4ca4b1f9e8ef17a6813fde4c7cd05a3 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 27 Nov 2019 17:09:07 -0800 Subject: Add default values for InputTarget struct The struct InputTarget has some default values for its fields, but not for all fields. Now that we are adding a FocusEntry, we don't want to deal with scale and such. To prevent undefined values in this struct, add default initializers. Bug: none Test: none Change-Id: I47660f81a642f55182dd90e91dc19e17f4e2f1dd --- services/inputflinger/dispatcher/InputTarget.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 5acf92b1b1..2e9bca251f 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -93,21 +93,22 @@ struct InputTarget { sp inputChannel; // Flags for the input target. - int32_t flags; + int32_t flags = 0; // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) - float xOffset, yOffset; + float xOffset = 0.0f; + float yOffset = 0.0f; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) - float globalScaleFactor; + float globalScaleFactor = 1.0f; float windowXScale = 1.0f; float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. - BitSet32 pointerIds; + BitSet32 pointerIds{}; }; std::string dispatchModeToString(int32_t dispatchMode); -- cgit v1.2.3-59-g8ed1b From f3bc1aa7a0923a8dc3bdeb5dd8127ab17b921e36 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 25 Nov 2019 13:48:53 -0800 Subject: Receive touch mode state from WindowManager Input needs to keep a copy of the touch mode state in order to tell apps about it. The state will be sent to apps at the same time as the focus change event. The code currently does nothing, but the state will be needed in a future CL. Bug: 70668286 Test: presubmit Change-Id: Ifab76977a0380370a753294d5a7504e3cec88af1 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 9 +++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 2 ++ .../inputflinger/dispatcher/include/InputDispatcherInterface.h | 8 ++++++++ 3 files changed, 19 insertions(+) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index aba5a30206..746908ba40 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -252,6 +252,10 @@ InputDispatcher::InputDispatcher(const sp& polic mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), + // mInTouchMode will be initialized by the WindowManager to the default device config. + // To avoid leaking stack in case that call never comes, and for tests, + // initialize it here anyways. + mInTouchMode(true), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); @@ -3538,6 +3542,11 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { mLooper->wake(); } +void InputDispatcher::setInTouchMode(bool inTouchMode) { + std::scoped_lock lock(mLock); + mInTouchMode = inTouchMode; +} + bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp& toToken) { if (fromToken == toToken) { if (DEBUG_FOCUS) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d21b0a1f64..5d7d812692 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -102,6 +102,7 @@ public: virtual void setFocusedDisplay(int32_t displayId) override; virtual void setInputDispatchMode(bool enabled, bool frozen) override; virtual void setInputFilterEnabled(bool enabled) override; + virtual void setInTouchMode(bool inTouchMode) override; virtual bool transferTouchFocus(const sp& fromToken, const sp& toToken) override; @@ -247,6 +248,7 @@ private: bool mDispatchEnabled GUARDED_BY(mLock); bool mDispatchFrozen GUARDED_BY(mLock); bool mInputFilterEnabled GUARDED_BY(mLock); + bool mInTouchMode GUARDED_BY(mLock); std::unordered_map>> mWindowHandlesByDisplay GUARDED_BY(mLock); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index ce7366f475..3082738a23 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -116,6 +116,14 @@ public: */ virtual void setInputFilterEnabled(bool enabled) = 0; + /** + * Set the touch mode state. + * Touch mode is a global state that apps may enter / exit based on specific + * user interactions with input devices. + * If true, the device is in touch mode. + */ + virtual void setInTouchMode(bool inTouchMode) = 0; + /* Transfers touch focus from one window to another window. * * Returns true on success. False if the window did not actually have touch focus. -- cgit v1.2.3-59-g8ed1b From 7feb2eaf82d88bd4d903f43776860015806274ce Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 25 Nov 2019 15:11:23 -0800 Subject: Add FocusEvent and InputMessage::Type::FOCUS FocusEvents will be consumed by InputConsumer on the app side. They will be used to notify app that focus has been gained / lost. They will also carry information about the current state of touch mode. Also add a new type of InputMessage with type FOCUS. This new data structure will be used to pass focus events to the apps from input across the socket. Bug: 70668286 Test: presubmit Change-Id: I88582c64ee41ecb49623b9b7f5c149eafa694788 --- include/android/input.h | 5 +- include/input/Input.h | 29 ++++++++++ include/input/InputTransport.h | 34 ++++++++--- libs/input/Input.cpp | 45 +++++++++++++++ libs/input/InputTransport.cpp | 67 +++++++++++++++++----- .../input/tests/InputPublisherAndConsumer_test.cpp | 43 ++++++++++++++ libs/input/tests/StructLayout_test.cpp | 5 ++ .../inputflinger/dispatcher/InputDispatcher.cpp | 2 +- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 9 files changed, 209 insertions(+), 25 deletions(-) (limited to 'services/inputflinger') diff --git a/include/android/input.h b/include/android/input.h index ce439c6d75..f51cd79504 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -158,7 +158,10 @@ enum { AINPUT_EVENT_TYPE_KEY = 1, /** Indicates that the input event is a motion event. */ - AINPUT_EVENT_TYPE_MOTION = 2 + AINPUT_EVENT_TYPE_MOTION = 2, + + /** Focus event */ + AINPUT_EVENT_TYPE_FOCUS = 3, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index a7e706ed18..f8718479f9 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -167,6 +167,8 @@ namespace android { class Parcel; #endif +const char* inputEventTypeToString(int32_t type); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -686,6 +688,28 @@ protected: Vector mSamplePointerCoords; }; +/* + * Focus events. + */ +class FocusEvent : public InputEvent { +public: + virtual ~FocusEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; } + + inline bool getHasFocus() const { return mHasFocus; } + + inline bool getInTouchMode() const { return mInTouchMode; } + + void initialize(bool hasFocus, bool inTouchMode); + + void initialize(const FocusEvent& from); + +protected: + bool mHasFocus; + bool mInTouchMode; +}; + /* * Input event factory. */ @@ -698,6 +722,7 @@ public: virtual KeyEvent* createKeyEvent() = 0; virtual MotionEvent* createMotionEvent() = 0; + virtual FocusEvent* createFocusEvent() = 0; }; /* @@ -711,10 +736,12 @@ public: virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } + virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; + FocusEvent mFocusEvent; }; /* @@ -727,6 +754,7 @@ public: virtual KeyEvent* createKeyEvent() override; virtual MotionEvent* createMotionEvent() override; + virtual FocusEvent* createFocusEvent() override; void recycle(InputEvent* event); @@ -735,6 +763,7 @@ private: std::queue> mKeyEventPool; std::queue> mMotionEventPool; + std::queue> mFocusEventPool; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d39ee250a5..ae47438ac8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -64,6 +64,7 @@ struct InputMessage { KEY, MOTION, FINISHED, + FOCUS, }; struct Header { @@ -92,9 +93,7 @@ struct InputMessage { uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); - inline size_t size() const { - return sizeof(Key); - } + inline size_t size() const { return sizeof(Key); } } key; struct Motion { @@ -110,7 +109,7 @@ struct InputMessage { int32_t metaState; int32_t buttonState; MotionClassification classification; // base type: uint8_t - uint8_t empty2[3]; + uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); float xOffset; @@ -148,10 +147,17 @@ struct InputMessage { uint32_t seq; uint32_t handled; // actually a bool, but we must maintain 8-byte alignment - inline size_t size() const { - return sizeof(Finished); - } + inline size_t size() const { return sizeof(Finished); } } finished; + + struct Focus { + uint32_t seq; + // The following two fields take up 4 bytes total + uint16_t hasFocus; // actually a bool + uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment + + inline size_t size() const { return sizeof(Focus); } + } focus; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -294,6 +300,15 @@ public: uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); + /* Publishes a focus event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode); + /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, * and whether the consumer handled the message. @@ -349,8 +364,8 @@ public: * Returns NO_MEMORY if the event could not be created. * Other errors probably indicate that the channel is broken. */ - status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -521,6 +536,7 @@ private: static void rewriteMessage(TouchState& state, InputMessage& msg); static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg); static void addSample(MotionEvent* event, const InputMessage* msg); static bool canAddSample(const Batch& batch, const InputMessage* msg); static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c7303efd13..8ccbc7f650 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #ifdef __ANDROID__ @@ -41,6 +42,21 @@ const char* motionClassificationToString(MotionClassification classification) { // --- InputEvent --- +const char* inputEventTypeToString(int32_t type) { + switch (type) { + case AINPUT_EVENT_TYPE_KEY: { + return "KEY"; + } + case AINPUT_EVENT_TYPE_MOTION: { + return "MOTION"; + } + case AINPUT_EVENT_TYPE_FOCUS: { + return "FOCUS"; + } + } + return "UNKNOWN"; +} + void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; @@ -587,6 +603,20 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +// --- FocusEvent --- + +void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { + InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE); + mHasFocus = hasFocus; + mInTouchMode = inTouchMode; +} + +void FocusEvent::initialize(const FocusEvent& from) { + InputEvent::initialize(from); + mHasFocus = from.mHasFocus; + mInTouchMode = from.mInTouchMode; +} // --- PooledInputEventFactory --- @@ -615,6 +645,15 @@ MotionEvent* PooledInputEventFactory::createMotionEvent() { return event; } +FocusEvent* PooledInputEventFactory::createFocusEvent() { + if (mFocusEventPool.empty()) { + return new FocusEvent(); + } + FocusEvent* event = mFocusEventPool.front().release(); + mFocusEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -629,6 +668,12 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_FOCUS: + if (mFocusEventPool.size() < mMaxPoolSize) { + mFocusEventPool.push(std::unique_ptr(static_cast(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index b7937dc95e..200e1f39d9 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -103,6 +103,8 @@ bool InputMessage::isValid(size_t actualSize) const { return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; case Type::FINISHED: return true; + case Type::FOCUS: + return true; } } return false; @@ -116,6 +118,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.motion.size(); case Type::FINISHED: return sizeof(Header) + body.finished.size(); + case Type::FOCUS: + return sizeof(Header) + body.focus.size(); } return sizeof(Header); } @@ -220,6 +224,12 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.finished.handled = body.finished.handled; break; } + case InputMessage::Type::FOCUS: { + msg->body.focus.seq = body.focus.seq; + msg->body.focus.hasFocus = body.focus.hasFocus; + msg->body.focus.inTouchMode = body.focus.inTouchMode; + break; + } } } @@ -529,6 +539,23 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", + mChannel->getName().c_str(), toString(hasFocus), + toString(inTouchMode)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::FOCUS; + msg.body.focus.seq = seq; + msg.body.focus.hasFocus = hasFocus ? 1 : 0; + msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; + return mChannel->sendMessage(&msg); +} + status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); @@ -565,8 +592,8 @@ bool InputConsumer::isTouchResamplingEnabled() { return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), toString(consumeBatches), frameTime); @@ -669,19 +696,19 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, break; } - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; - updateTouchState(mMsg); - initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; - *outEvent = motionEvent; + updateTouchState(mMsg); + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } - break; + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } + break; } case InputMessage::Type::FINISHED: { @@ -689,6 +716,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, "InputConsumer!"); break; } + + case InputMessage::Type::FOCUS: { + FocusEvent* focusEvent = factory->createFocusEvent(); + if (!focusEvent) return NO_MEMORY; + + initializeFocusEvent(focusEvent, &mMsg); + *outSeq = mMsg.body.focus.seq; + *outEvent = focusEvent; + break; + } } } return OK; @@ -1113,6 +1150,10 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) msg->body.key.eventTime); } +void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { + event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index a362f3281d..2fc77e97a0 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -60,6 +60,7 @@ protected: void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); + void PublishAndConsumeFocusEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -256,6 +257,43 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } +void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { + status_t status; + + constexpr uint32_t seq = 15; + constexpr bool hasFocus = true; + constexpr bool inTouchMode = true; + + status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode); + ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType()) + << "consumer should have returned a focus event"; + + FocusEvent* focusEvent = static_cast(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; +} + TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } @@ -264,6 +302,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -322,6 +364,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 0fb6cfc204..9ab0dba08d 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -69,6 +69,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } @@ -87,6 +91,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); + static_assert(sizeof(InputMessage::Body::Focus) == 8); } } // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 746908ba40..9c0e08e297 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3036,7 +3036,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } default: - ALOGW("Cannot inject event of type %d", event->getType()); + ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType())); return INPUT_EVENT_INJECTION_FAILED; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ebce47f1e7..4f282611d5 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -410,7 +411,8 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << ": event type should match."; + << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); -- cgit v1.2.3-59-g8ed1b From 2bfa905445b677143f20056561661ee134792184 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 21 Nov 2019 18:10:54 -0800 Subject: Wait until dispatcher is idle Add a mechanism for waiting until the dispatcher is idle. This will allow tests to not have to wait until a specific timeout is reached when asserting that a callback did not happen. For example, the current test approach is like this: 1) do something 2) monitor policy for a certain amount of time 3) check that the thing you are interested in did not happen. With this approach, we will modify this behaviour to be as follows: 1) do something 2) wait until dispatcher is idle 3) check that the thing didn't happen. In this case, the dispatcher is idle condition will happen sooner than the timeout in the original approach. This will help keep the tests fast. Bug: 70668286 Test: atest inputflinger_tests Change-Id: I2ac5e38ccbc388fe6d94832405b68ef9e52d25f4 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 24 ++++ services/inputflinger/dispatcher/InputDispatcher.h | 2 + .../dispatcher/include/InputDispatcherInterface.h | 8 ++ .../inputflinger/tests/InputDispatcher_test.cpp | 138 +++++++++++++++++---- 4 files changed, 147 insertions(+), 25 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9c0e08e297..b9bec441b0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -298,6 +298,12 @@ void InputDispatcher::dispatchOnce() { if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } + + // We are about to enter an infinitely long sleep, because we have no commands or + // pending or queued events + if (nextWakeupTime == LONG_LONG_MAX) { + mDispatcherEnteredIdle.notify_all(); + } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) @@ -4582,4 +4588,22 @@ void InputDispatcher::monitor() { mDispatcherIsAlive.wait(_l); } +/** + * Wake up the dispatcher and wait until it processes all events and commands. + * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so + * this method can be safely called from any thread, as long as you've ensured that + * the work you are interested in completing has already been queued. + */ +bool InputDispatcher::waitForIdle() { + /** + * Timeout should represent the longest possible time that a device might spend processing + * events and commands. + */ + constexpr std::chrono::duration TIMEOUT = 100ms; + std::unique_lock lock(mLock); + mLooper->wake(); + std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT); + return result == std::cv_status::no_timeout; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 5d7d812692..38f86742b3 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -81,6 +81,7 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; + virtual bool waitForIdle() override; virtual void dispatchOnce() override; @@ -129,6 +130,7 @@ private: std::mutex mLock; std::condition_variable mDispatcherIsAlive; + std::condition_variable mDispatcherEnteredIdle; sp mLooper; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 3082738a23..db9fba6fcc 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -63,6 +63,14 @@ public: /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ virtual void monitor() = 0; + /** + * Wait until dispatcher is idle. That means, there are no further events to be processed, + * and all of the policy callbacks have been completed. + * Return true if the dispatcher is idle. + * Return false if the timeout waiting for the dispatcher to become idle has expired. + */ + virtual bool waitForIdle() = 0; + /* Runs a single iteration of the dispatch loop. * Nominally processes one queued event, a timeout, or a response from an input consumer. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 4f282611d5..c8d39cf630 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -51,7 +51,6 @@ protected: public: FakeInputDispatcherPolicy() { - mOnPointerDownToken.clear(); } void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { @@ -66,17 +65,41 @@ public: void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); } + void assertNotifyConfigurationChangedWasCalled(nsecs_t when) { + ASSERT_TRUE(mConfigurationChangedTime) + << "Timed out waiting for configuration changed call"; + ASSERT_EQ(*mConfigurationChangedTime, when); + mConfigurationChangedTime = std::nullopt; + } + + void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { + ASSERT_TRUE(mLastNotifySwitch); + // We do not check sequenceNum because it is not exposed to the policy + EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime); + EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags); + EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues); + EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask); + mLastNotifySwitch = std::nullopt; + } + void assertOnPointerDownEquals(const sp& touchedToken) { - ASSERT_EQ(mOnPointerDownToken, touchedToken) - << "Expected token from onPointerDownOutsideFocus was not matched"; - reset(); + ASSERT_EQ(touchedToken, mOnPointerDownToken); + mOnPointerDownToken.clear(); + } + + void assertOnPointerDownWasNotCalled() { + ASSERT_TRUE(mOnPointerDownToken == nullptr) + << "Expected onPointerDownOutsideFocus to not have been called"; } private: std::unique_ptr mFilteredEvent; + std::optional mConfigurationChangedTime; sp mOnPointerDownToken; + std::optional mLastNotifySwitch; - virtual void notifyConfigurationChanged(nsecs_t) { + virtual void notifyConfigurationChanged(nsecs_t when) override { + mConfigurationChangedTime = when; } virtual nsecs_t notifyANR(const sp&, @@ -128,7 +151,13 @@ private: return false; } - virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) { + virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) override { + /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is + * essentially a passthrough for notifySwitch. + */ + mLastNotifySwitch = + NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask); } virtual void pokeUserActivity(nsecs_t, int32_t) { @@ -163,8 +192,6 @@ private: mFilteredEvent = nullptr; } - - void reset() { mOnPointerDownToken.clear(); } }; @@ -351,9 +378,30 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with duplicate pointer ids."; } +/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */ + +TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { + constexpr nsecs_t eventTime = 20; + NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime); + mDispatcher->notifyConfigurationChanged(&args); + ASSERT_TRUE(mDispatcher->waitForIdle()); + + mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime); +} + +TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { + NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/, + 1 /*switchValues*/, 2 /*switchMask*/); + mDispatcher->notifySwitch(&args); + + // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener + args.policyFlags |= POLICY_FLAG_TRUSTED; + mFakePolicy->assertNotifySwitchWasCalled(args); +} + // --- InputDispatcherTest SetInputWindowTest --- -static const int32_t INJECT_EVENT_TIMEOUT = 500; -static const int32_t DISPATCHING_TIMEOUT = 100; +static constexpr int32_t INJECT_EVENT_TIMEOUT = 500; +static constexpr int32_t DISPATCHING_TIMEOUT = 100; class FakeApplicationHandle : public InputApplicationHandle { public: @@ -536,9 +584,7 @@ public: InputWindowHandle::releaseChannel(); } protected: - virtual bool handled() { - return true; - } + virtual bool handled() override { return true; } bool mFocused; Rect mFrame; @@ -761,6 +807,51 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { windowRight->assertNoEvents(); } +TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFocus(); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); + + // When device reset happens, that key stream should be terminated with FLAG_CANCELED + // on the app side. + NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID); + mDispatcher->notifyDeviceReset(&args); + window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, + AKEY_EVENT_FLAG_CANCELED); +} + +TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + + // Window should receive motion down event. + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // When device reset happens, that motion stream should be terminated with ACTION_CANCEL + // on the app side. + NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID); + mDispatcher->notifyDeviceReset(&args); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: @@ -924,7 +1015,7 @@ protected: motionArgs = generateMotionArgs( AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId); mDispatcher->notifyMotion(&motionArgs); - + ASSERT_TRUE(mDispatcher->waitForIdle()); if (expectToBeFiltered) { mFakePolicy->assertFilterInputEventWasCalled(motionArgs); } else { @@ -939,6 +1030,7 @@ protected: mDispatcher->notifyKey(&keyArgs); keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP); mDispatcher->notifyKey(&keyArgs); + ASSERT_TRUE(mDispatcher->waitForIdle()); if (expectToBeFiltered) { mFakePolicy->assertFilterInputEventWasCalled(keyArgs); @@ -1029,9 +1121,8 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Succe ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - // Call monitor to wait for the command queue to get flushed. - mDispatcher->monitor(); + ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken()); } @@ -1042,10 +1133,9 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - // Call monitor to wait for the command queue to get flushed. - mDispatcher->monitor(); - mFakePolicy->assertOnPointerDownEquals(nullptr); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertOnPointerDownWasNotCalled(); } // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't @@ -1053,10 +1143,9 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - // Call monitor to wait for the command queue to get flushed. - mDispatcher->monitor(); - mFakePolicy->assertOnPointerDownEquals(nullptr); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertOnPointerDownWasNotCalled(); } // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action @@ -1068,10 +1157,9 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mFocusedWindowTouchPoint, mFocusedWindowTouchPoint)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - // Call monitor to wait for the command queue to get flushed. - mDispatcher->monitor(); - mFakePolicy->assertOnPointerDownEquals(nullptr); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertOnPointerDownWasNotCalled(); } } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 28efc19d395bca5c747d71d392dfbeccf42b00be Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 5 Nov 2019 01:10:04 +0000 Subject: Reland "Let InputReader handle its own thread" This CL was first landed in Ic732436d4f00a831e317be1f16ac106a11652cff but was reverted due to flaky tests. The flaky tests were caused by races between the instrumented test classes and the InputReader class under test, which now runs in a new thread. In addition to re-landing the change, this CL ensures that InputReader does not start its own thread in its unit tests, but instead keep using the loopOnce method that is exposed for testing. Bug: 130819454 Test: atest inputflinger_tests Test: Touch input works on crosshatch Change-Id: I5bbfc68b6be6745efc8b7106a9292ee3cb431c9c --- services/inputflinger/InputManager.cpp | 9 ++-- services/inputflinger/InputManager.h | 11 ++--- services/inputflinger/InputReaderBase.cpp | 14 ------ services/inputflinger/include/InputReaderBase.h | 48 ++++++++++---------- services/inputflinger/reader/InputReader.cpp | 47 ++++++++++++++++++- services/inputflinger/reader/include/InputReader.h | 16 +++++-- services/inputflinger/tests/InputReader_test.cpp | 53 +++++++++++----------- 7 files changed, 118 insertions(+), 80 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index e7640dd6af..1043390f84 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -46,7 +46,6 @@ InputManager::~InputManager() { } void InputManager::initialize() { - mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); } @@ -57,9 +56,9 @@ status_t InputManager::start() { return result; } - result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + result = mReader->start(); if (result) { - ALOGE("Could not start InputReader thread due to error %d.", result); + ALOGE("Could not start InputReader due to error %d.", result); mDispatcherThread->requestExit(); return result; @@ -69,9 +68,9 @@ status_t InputManager::start() { } status_t InputManager::stop() { - status_t result = mReaderThread->requestExitAndWait(); + status_t result = mReader->stop(); if (result) { - ALOGW("Could not stop InputReader thread due to error %d.", result); + ALOGW("Could not stop InputReader due to error %d.", result); } result = mDispatcherThread->requestExitAndWait(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 40f66d82f4..2a7ed0ff44 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -43,15 +43,15 @@ class InputDispatcherThread; /* * The input manager is the core of the system event processing. * - * The input manager uses two threads. + * The input manager has two components. * - * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, - * applies policy, and posts messages to a queue managed by the DispatcherThread. + * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies + * policy, and posts messages to a queue managed by the InputDispatcherThread. * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the * queue and asynchronously dispatches them to applications. * - * By design, the InputReaderThread class and InputDispatcherThread class do not share any - * internal state. Moreover, all communication is done one way from the InputReaderThread + * By design, the InputReader class and InputDispatcherThread class do not share any + * internal state. Moreover, all communication is done one way from the InputReader * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. * @@ -102,7 +102,6 @@ public: private: sp mReader; - sp mReaderThread; sp mClassifier; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 0422d8342b..2d6f2c1cc9 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -33,20 +33,6 @@ using android::base::StringPrintf; namespace android { -// --- InputReaderThread --- - -InputReaderThread::InputReaderThread(const sp& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} - -InputReaderThread::~InputReaderThread() { -} - -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); - return true; -} - // --- InputReaderConfiguration --- std::string InputReaderConfiguration::changesToString(uint32_t changes) { diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 5d576b94f3..56c0a7356d 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -19,12 +19,12 @@ #include "PointerControllerInterface.h" +#include #include #include -#include #include #include -#include +#include #include #include @@ -44,7 +44,16 @@ namespace android { -/* Processes raw input events and sends cooked event data to an input listener. */ +// --- InputReaderInterface --- + +/* The interface for the InputReader shared library. + * + * Manages one or more threads that process raw input events and sends cooked event data to an + * input listener. + * + * The implementation must guarantee thread safety for this interface. However, since the input + * listener is NOT thread safe, all calls to the listener must happen from the same thread. + */ class InputReaderInterface : public virtual RefBase { protected: InputReaderInterface() { } @@ -56,18 +65,17 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the reader has not deadlocked. */ + /* Called by the heartbeat to ensures that the reader has not deadlocked. */ virtual void monitor() = 0; /* Returns true if the input device is enabled. */ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0; - /* Runs a single iteration of the processing loop. - * Nominally reads and processes one incoming message from the EventHub. - * - * This method should be called on the input reader thread. - */ - virtual void loopOnce() = 0; + /* Makes the reader start processing events from the kernel. */ + virtual status_t start() = 0; + + /* Makes the reader stop processing any more events. */ + virtual status_t stop() = 0; /* Gets information about all input devices. * @@ -104,17 +112,7 @@ public: virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; }; -/* Reads raw events from the event hub and processes them, endlessly. */ -class InputReaderThread : public Thread { -public: - explicit InputReaderThread(const sp& reader); - virtual ~InputReaderThread(); - -private: - sp mReader; - - virtual bool threadLoop(); -}; +// --- InputReaderConfiguration --- /* * Input reader configuration. @@ -285,6 +283,8 @@ private: std::vector mDisplays; }; +// --- TouchAffineTransformation --- + struct TouchAffineTransformation { float x_scale; float x_ymix; @@ -307,6 +307,8 @@ struct TouchAffineTransformation { void applyTo(float& x, float& y) const; }; +// --- InputReaderPolicyInterface --- + /* * Input reader policy interface. * @@ -316,8 +318,8 @@ struct TouchAffineTransformation { * The actual implementation is partially supported by callbacks into the DVM * via JNI. This interface is also mocked in the unit tests. * - * These methods must NOT re-enter the input reader since they may be called while - * holding the input reader lock. + * These methods will NOT re-enter the input reader interface, so they may be called from + * any method in the input reader interface. */ class InputReaderPolicyInterface : public virtual RefBase { protected: diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 1c5adc3a85..05f0db1329 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,16 +38,38 @@ #include #include +#include #include #include #include - +#include using android::base::StringPrintf; namespace android { +// --- InputReader::InputReaderThread --- + +/* Thread that reads raw events from the event hub and processes them, endlessly. */ +class InputReader::InputReaderThread : public Thread { +public: + explicit InputReaderThread(InputReader* reader) + : Thread(/* canCallJava */ true), mReader(reader) {} + + ~InputReaderThread() {} + +private: + InputReader* mReader; + + bool threadLoop() override { + mReader->loopOnce(); + return true; + } +}; + +// --- InputReader --- + InputReader::InputReader(std::shared_ptr eventHub, const sp& policy, const sp& listener) @@ -61,6 +83,7 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); + mThread = new InputReaderThread(this); { // acquire lock AutoMutex _l(mLock); @@ -76,6 +99,28 @@ InputReader::~InputReader() { } } +status_t InputReader::start() { + if (mThread->isRunning()) { + return ALREADY_EXISTS; + } + return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY); +} + +status_t InputReader::stop() { + if (!mThread->isRunning()) { + return OK; + } + if (gettid() == mThread->getTid()) { + ALOGE("InputReader can only be stopped from outside of the InputReaderThread!"); + return INVALID_OPERATION; + } + // Directly calling requestExitAndWait() causes the thread to not exit + // if mEventHub is waiting for a long timeout. + mThread->requestExit(); + mEventHub->wake(); + return mThread->requestExitAndWait(); +} + void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 557eb3b7c4..502490601e 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -38,12 +38,12 @@ struct StylusState; * that it sends to the input listener. Some functions of the input reader, such as early * event filtering in low power states, are controlled by a separate policy object. * - * The InputReader owns a collection of InputMappers. Most of the work it does happens - * on the input reader thread but the InputReader can receive queries from other system + * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where + * most of the work happens, but the InputReader can receive queries from other system * components running on arbitrary threads. To keep things manageable, the InputReader * uses a single Mutex to guard its state. The Mutex may be held while calling into the * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. + * InputListener. All calls to InputListener must happen from InputReader's thread. */ class InputReader : public InputReaderInterface { public: @@ -55,7 +55,8 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; - virtual void loopOnce() override; + virtual status_t start() override; + virtual status_t stop() override; virtual void getInputDevices(std::vector& outInputDevices) override; @@ -86,6 +87,10 @@ protected: const InputDeviceIdentifier& identifier, uint32_t classes); + // With each iteration of the loop, InputReader reads and processes one incoming message from + // the EventHub. + void loopOnce(); + class ContextImpl : public InputReaderContext { InputReader* mReader; @@ -111,6 +116,9 @@ protected: friend class ContextImpl; private: + class InputReaderThread; + sp mThread; + Mutex mLock; Condition mReaderIsAliveCondition; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c1c912214a..d6624c9f5a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1113,6 +1113,9 @@ public: classes); } + // Make the protected loopOnce method accessible to tests. + using InputReader::loopOnce; + protected: virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, @@ -1133,12 +1136,8 @@ class InputReaderPolicyTest : public testing::Test { protected: sp mFakePolicy; - virtual void SetUp() { - mFakePolicy = new FakeInputReaderPolicy(); - } - virtual void TearDown() { - mFakePolicy.clear(); - } + virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } + virtual void TearDown() override { mFakePolicy.clear(); } }; /** @@ -1321,19 +1320,18 @@ protected: sp mFakeListener; sp mFakePolicy; std::shared_ptr mFakeEventHub; - sp mReader; + std::unique_ptr mReader; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener); + mReader = std::make_unique(mFakeEventHub, mFakePolicy, + mFakeListener); } - virtual void TearDown() { - mReader.clear(); - + virtual void TearDown() override { mFakeListener.clear(); mFakePolicy.clear(); } @@ -1354,16 +1352,12 @@ protected: void disableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->addDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } void enableDevice(int32_t deviceId, InputDevice* device) { mFakePolicy->removeDisabledDevice(deviceId); - configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device); - } - - void configureDevice(uint32_t changes, InputDevice* device) { - device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, @@ -1417,7 +1411,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); @@ -1425,7 +1418,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); @@ -1438,7 +1430,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { enableDevice(deviceId, device); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); } @@ -1629,7 +1620,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, "fake", deviceClass, nullptr); const uint8_t hdmi1 = 1; @@ -1637,13 +1627,21 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); // Add default and second display. + mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); + + // Add the device, and make sure all of the callbacks are triggered. + // The device is added after the input port associations are processed since + // we do not yet support dynamic device-to-display associations. + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); @@ -1652,6 +1650,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Can't dispatch event from a disabled device. disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1674,7 +1673,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1688,7 +1687,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1912,7 +1911,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1926,7 +1925,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2589,7 +2588,7 @@ protected: sp mFakePointerController; - virtual void SetUp() { + virtual void SetUp() override { InputMapperTest::SetUp(); mFakePointerController = new FakePointerController(); -- cgit v1.2.3-59-g8ed1b From 3608aadd736d28b9594dea54cd07cc628955b4e0 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 2 Oct 2019 17:08:26 -0700 Subject: Let InputDispatcher handle its own thread We move the threading logic from InputManger to InputDispatcher by removing dispatchOnce() method from InputDispatcherInterface and adding a start() and stop() method in its place. Bug: 130819454 Test: atest inputflinger_tests Test: Touch input works on crosshatch Change-Id: I1d06be2330a2f8b9261fd5c5323a486d6aa544e8 --- services/inputflinger/InputManager.cpp | 18 +++++----- services/inputflinger/InputManager.h | 11 +++--- services/inputflinger/dispatcher/Android.bp | 1 - .../inputflinger/dispatcher/InputDispatcher.cpp | 42 ++++++++++++++++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 12 +++++-- .../dispatcher/InputDispatcherThread.cpp | 33 ----------------- .../dispatcher/include/InputDispatcherInterface.h | 11 +++--- .../dispatcher/include/InputDispatcherThread.h | 41 --------------------- .../inputflinger/tests/InputDispatcher_test.cpp | 21 +++++------ 9 files changed, 79 insertions(+), 111 deletions(-) delete mode 100644 services/inputflinger/dispatcher/InputDispatcherThread.cpp delete mode 100644 services/inputflinger/dispatcher/include/InputDispatcherThread.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 1043390f84..c7c61cf1ef 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -20,7 +20,6 @@ #include "InputManager.h" #include "InputDispatcherFactory.h" -#include "InputDispatcherThread.h" #include "InputReaderFactory.h" #include @@ -38,19 +37,14 @@ InputManager::InputManager( mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); mReader = createInputReader(readerPolicy, mClassifier); - initialize(); } InputManager::~InputManager() { stop(); } -void InputManager::initialize() { - mDispatcherThread = new InputDispatcherThread(mDispatcher); -} - status_t InputManager::start() { - status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); + status_t result = mDispatcher->start(); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; @@ -60,7 +54,7 @@ status_t InputManager::start() { if (result) { ALOGE("Could not start InputReader due to error %d.", result); - mDispatcherThread->requestExit(); + mDispatcher->stop(); return result; } @@ -68,17 +62,21 @@ status_t InputManager::start() { } status_t InputManager::stop() { + status_t status = OK; + status_t result = mReader->stop(); if (result) { ALOGW("Could not stop InputReader due to error %d.", result); + status = result; } - result = mDispatcherThread->requestExitAndWait(); + result = mDispatcher->stop(); if (result) { ALOGW("Could not stop InputDispatcher thread due to error %d.", result); + status = result; } - return OK; + return status; } sp InputManager::getReader() { diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 2a7ed0ff44..586097f6a3 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -47,10 +47,10 @@ class InputDispatcherThread; * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies * policy, and posts messages to a queue managed by the InputDispatcherThread. - * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the + * 2. The InputDispatcher class starts a thread that waits for new events on the * queue and asynchronously dispatches them to applications. * - * By design, the InputReader class and InputDispatcherThread class do not share any + * By design, the InputReader class and InputDispatcher class do not share any * internal state. Moreover, all communication is done one way from the InputReader * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. @@ -65,10 +65,10 @@ protected: virtual ~InputManagerInterface() { } public: - /* Starts the input manager threads. */ + /* Starts the input threads. */ virtual status_t start() = 0; - /* Stops the input manager threads and waits for them to exit. */ + /* Stops the input threads and waits for them to exit. */ virtual status_t stop() = 0; /* Gets the input reader. */ @@ -106,9 +106,6 @@ private: sp mClassifier; sp mDispatcher; - sp mDispatcherThread; - - void initialize(); }; } // namespace android diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 9185e00272..a556aad553 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -21,7 +21,6 @@ cc_library_static { "InjectionState.cpp", "InputDispatcher.cpp", "InputDispatcherFactory.cpp", - "InputDispatcherThread.cpp", "InputState.cpp", "InputTarget.cpp", "Monitor.cpp", diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index b9bec441b0..dcb3ebcbd7 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -240,6 +240,24 @@ static bool removeByValue(std::unordered_map& map, const V& value) { return removed; } +// --- InputDispatcherThread --- + +class InputDispatcher::InputDispatcherThread : public Thread { +public: + explicit InputDispatcherThread(InputDispatcher* dispatcher) + : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {} + + ~InputDispatcherThread() {} + +private: + InputDispatcher* mDispatcher; + + virtual bool threadLoop() override { + mDispatcher->dispatchOnce(); + return true; + } +}; + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp& policy) @@ -264,6 +282,8 @@ InputDispatcher::InputDispatcher(const sp& polic mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); + + mThread = new InputDispatcherThread(this); } InputDispatcher::~InputDispatcher() { @@ -281,6 +301,28 @@ InputDispatcher::~InputDispatcher() { } } +status_t InputDispatcher::start() { + if (mThread->isRunning()) { + return ALREADY_EXISTS; + } + return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); +} + +status_t InputDispatcher::stop() { + if (!mThread->isRunning()) { + return OK; + } + if (gettid() == mThread->getTid()) { + ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!"); + return INVALID_OPERATION; + } + // Directly calling requestExitAndWait() causes the thread to not exit + // if mLooper is waiting for a long timeout. + mThread->requestExit(); + mLooper->wake(); + return mThread->requestExitAndWait(); +} + void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 38f86742b3..96a09e37df 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -82,8 +82,8 @@ public: virtual void dump(std::string& dump) override; virtual void monitor() override; virtual bool waitForIdle() override; - - virtual void dispatchOnce() override; + virtual status_t start() override; + virtual status_t stop() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -124,6 +124,9 @@ private: STALE, }; + class InputDispatcherThread; + sp mThread; + sp mPolicy; android::InputDispatcherConfiguration mConfig; @@ -141,6 +144,11 @@ private: DropReason mLastDropReason GUARDED_BY(mLock); + // With each iteration, InputDispatcher nominally processes one queued event, + // a timeout, or a response from an input consumer. + // This method should only be called on the input dispatcher's own thread. + void dispatchOnce(); + void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp deleted file mode 100644 index 18b1b8c10a..0000000000 --- a/services/inputflinger/dispatcher/InputDispatcherThread.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InputDispatcherThread.h" - -#include "InputDispatcherInterface.h" - -namespace android { - -InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) - : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {} - -InputDispatcherThread::~InputDispatcherThread() {} - -bool InputDispatcherThread::threadLoop() { - mDispatcher->dispatchOnce(); - return true; -} - -} // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index db9fba6fcc..3424f4ce84 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -71,12 +71,15 @@ public: */ virtual bool waitForIdle() = 0; - /* Runs a single iteration of the dispatch loop. - * Nominally processes one queued event, a timeout, or a response from an input consumer. + /* Make the dispatcher start processing events. * - * This method should only be called on the input dispatcher thread. + * The dispatcher will start consuming events from the InputListenerInterface + * in the order that they were received. */ - virtual void dispatchOnce() = 0; + virtual status_t start() = 0; + + /* Makes the dispatcher stop processing events. */ + virtual status_t stop() = 0; /* Injects an input event and optionally waits for sync. * The synchronization mode determines whether the method blocks while waiting for diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h deleted file mode 100644 index 2604959656..0000000000 --- a/services/inputflinger/dispatcher/include/InputDispatcherThread.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H -#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H - -#include -#include - -namespace android { - -class InputDispatcherInterface; - -/* Enqueues and dispatches input events, endlessly. */ -class InputDispatcherThread : public Thread { -public: - explicit InputDispatcherThread(const sp& dispatcher); - ~InputDispatcherThread(); - -private: - virtual bool threadLoop(); - - sp mDispatcher; -}; - -} // namespace android - -#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c8d39cf630..d861a5f623 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,8 +16,6 @@ #include "../dispatcher/InputDispatcher.h" -#include - #include #include @@ -201,20 +199,17 @@ class InputDispatcherTest : public testing::Test { protected: sp mFakePolicy; sp mDispatcher; - sp mDispatcherThread; - virtual void SetUp() { + virtual void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); mDispatcher = new InputDispatcher(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); //Start InputDispatcher thread - mDispatcherThread = new InputDispatcherThread(mDispatcher); - mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY); + ASSERT_EQ(OK, mDispatcher->start()); } - virtual void TearDown() { - mDispatcherThread->requestExit(); - mDispatcherThread.clear(); + virtual void TearDown() override { + ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.clear(); mDispatcher.clear(); } @@ -856,7 +851,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: static constexpr int32_t SECOND_DISPLAY_ID = 1; - virtual void SetUp() { + virtual void SetUp() override { InputDispatcherTest::SetUp(); application1 = new FakeApplicationHandle(); @@ -880,7 +875,7 @@ public: mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); } - virtual void TearDown() { + virtual void TearDown() override { InputDispatcherTest::TearDown(); application1.clear(); @@ -1076,7 +1071,7 @@ TEST_F(InputFilterTest, KeyEvent_InputFilter) { } class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { - virtual void SetUp() { + virtual void SetUp() override { InputDispatcherTest::SetUp(); sp application = new FakeApplicationHandle(); @@ -1101,7 +1096,7 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); } - virtual void TearDown() { + virtual void TearDown() override { InputDispatcherTest::TearDown(); mUnfocusedWindow.clear(); -- cgit v1.2.3-59-g8ed1b From 3a240c441917de16db22705ce79a39b6a93cd792 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 10 Dec 2019 20:53:41 +0000 Subject: Check whether channel has been removed before sending cancelation. When a gesture monitor pilfers pointers we synthesize cancelation events for all currently active windows. It's possible, however, that a window may have been removed between the beginning of the touch stream and now, so we have to check whether the corresponding channel still exists. Bug: 145891806 Test: atest InputDispatcher_test.cpp Change-Id: If16191b02bbb35886171c402b9bb74f547bdf94a --- .../inputflinger/dispatcher/InputDispatcher.cpp | 4 +- .../inputflinger/tests/InputDispatcher_test.cpp | 94 +++++++++++++++++++--- 2 files changed, 88 insertions(+), 10 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index dcb3ebcbd7..5a49b5e16a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4115,7 +4115,9 @@ status_t InputDispatcher::pilferPointers(const sp& token) { options.displayId = displayId; for (const TouchedWindow& window : state.windows) { sp channel = getInputChannelLocked(window.windowHandle->getToken()); - synthesizeCancelationEventsForInputChannelLocked(channel, options); + if (channel != nullptr) { + synthesizeCancelationEventsForInputChannelLocked(channel, options); + } } // Then clear the current touch state so we stop dispatching to them as well. state.filterNonMonitors(); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index d861a5f623..0701f3b3ee 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -488,6 +488,11 @@ public: expectedFlags); } + void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, + expectedFlags); + } + void assertNoEvents() { InputEvent* event = consume(); ASSERT_EQ(nullptr, event) @@ -639,6 +644,11 @@ static int32_t injectMotionDown(const sp& dispatcher, int32_t s return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y); } +static int32_t injectMotionUp(const sp& dispatcher, int32_t source, + int32_t displayId, int32_t x = 100, int32_t y = 200) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y); +} + static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. @@ -847,6 +857,81 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } +class FakeMonitorReceiver : public FakeInputReceiver, public RefBase { +public: + FakeMonitorReceiver(const sp& dispatcher, const std::string name, + int32_t displayId, bool isGestureMonitor = false) + : FakeInputReceiver(dispatcher, name, displayId) { + mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor); + } + + sp getToken() { return mServerChannel->getConnectionToken(); } +}; + +// Tests for gesture monitors +TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + sp monitor = + new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT); +} + +TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocus(); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + sp monitor = + new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); + monitor->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + sp monitor = + new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + window->releaseChannel(); + + mDispatcher->pilferPointers(monitor->getToken()); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: @@ -934,15 +1019,6 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) windowInSecondary->assertNoEvents(); } -class FakeMonitorReceiver : public FakeInputReceiver, public RefBase { -public: - FakeMonitorReceiver(const sp& dispatcher, const std::string name, - int32_t displayId, bool isGestureMonitor = false) - : FakeInputReceiver(dispatcher, name, displayId) { - mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor); - } -}; - // Test per-display input monitors for motion event. TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { sp monitorInPrimary = -- cgit v1.2.3-59-g8ed1b From d041c5d9e162fbac4ef92c1d84a1bcd035fb8455 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 3 May 2019 17:11:33 -0700 Subject: Default TV remote keys to no-wake For Android TV, we want the default behavior of keys to not wake up the device. We want to be able to whitelist keys if wake is desired. This differs from the behavior on mobile where the default is to wake the device unless its a media key. We separate by means of the idc file. Specifying "keyboard.doNotWakeByDefault=1" in the idc would indicate the device is a TV remote and should go through corresponding path. Bug: 144979700 Test: Change idc/kl files. Rebuild and examine 'dumpsys input' on key presses. atest inputflinger_tests Change-Id: I4168c4c21fd901ca0402e0211f636b06234b9a85 --- include/input/InputEventLabels.h | 11 ++- .../reader/mapper/KeyboardInputMapper.cpp | 11 ++- .../reader/mapper/KeyboardInputMapper.h | 1 + services/inputflinger/tests/InputReader_test.cpp | 78 ++++++++++++++++++++++ 4 files changed, 92 insertions(+), 9 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index eaa562bb7b..b327d76b1c 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -405,13 +405,12 @@ static const InputEventLabel LEDS[] = { { nullptr, 0 } }; -static const InputEventLabel FLAGS[] = { - DEFINE_FLAG(VIRTUAL), - DEFINE_FLAG(FUNCTION), - DEFINE_FLAG(GESTURE), +static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL), + DEFINE_FLAG(FUNCTION), + DEFINE_FLAG(GESTURE), + DEFINE_FLAG(WAKE), - { nullptr, 0 } -}; + {nullptr, 0}}; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { while (list->literal) { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index f51d4a0d4a..348a7ada0e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -183,6 +183,9 @@ void KeyboardInputMapper::configureParameters() { mParameters.handlesKeyRepeat = false; config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); + + mParameters.doNotWakeByDefault = false; + config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault); } void KeyboardInputMapper::dumpParameters(std::string& dump) { @@ -331,10 +334,12 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, // Key down on external an keyboard should wake the device. // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. + // For internal keyboards and devices for which the default wake behavior is explicitly + // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each + // wake key individually. // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault && + !isMediaKey(keyCode)) { policyFlags |= POLICY_FLAG_WAKE; } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index de2a377e9a..badbcb26cf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -73,6 +73,7 @@ private: struct Parameters { bool orientationAware; bool handlesKeyRepeat; + bool doNotWakeByDefault; } mParameters; void configureParameters(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d6624c9f5a..1fc8217df8 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2580,6 +2580,84 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { AKEYCODE_DPAD_LEFT, newDisplayId)); } +TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { + // For external devices, non-media keys will trigger wake on key down. Media keys need to be + // marked as WAKE in the keylayout file to trigger wake. + mDevice->setExternal(true); + + mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addMapperAndConfigure(mapper); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); +} + +TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) { + // Tv Remote key's wake behavior is prescribed by the keylayout file. + mDevice->setExternal(true); + + mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); + addMapperAndConfigure(mapper); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); +} + // --- CursorInputMapperTest --- class CursorInputMapperTest : public InputMapperTest { -- cgit v1.2.3-59-g8ed1b From 8588cc99829d4f02ecff1fd5e4d3d8a719402949 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 12 Dec 2018 18:17:58 -0800 Subject: Native tests for EventHub Currently, there is no test coverage for EventHub. Add some basic tests for EventHub here. This should establish a place for future EventHub tests. Test: atest -a inputflinger_tests Bug: 62940136 Bug: 111431676 Change-Id: If7d1da814b1b296bd731dd82d227256067d085fd --- services/inputflinger/tests/Android.bp | 2 + services/inputflinger/tests/EventHub_test.cpp | 238 ++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 services/inputflinger/tests/EventHub_test.cpp (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index ada2266333..09ecb13b0a 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -4,6 +4,7 @@ cc_test { name: "inputflinger_tests", srcs: [ "BlockingQueue_test.cpp", + "EventHub_test.cpp", "TestInputListener.cpp", "InputClassifier_test.cpp", "InputClassifierConverter_test.cpp", @@ -36,4 +37,5 @@ cc_test { header_libs: [ "libinputreader_headers", ], + require_root: true, } diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp new file mode 100644 index 0000000000..6504738fc7 --- /dev/null +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventHub.h" + +#include +#include +#include +#include +#include +#include + +#define TAG "EventHub_test" + +using android::EventHub; +using android::EventHubInterface; +using android::InputDeviceIdentifier; +using android::RawEvent; +using android::sp; +using android::base::StringPrintf; +using std::chrono_literals::operator""ms; + +static constexpr bool DEBUG = false; +static const char* DEVICE_NAME = "EventHub Test Device"; + +static void dumpEvents(const std::vector& events) { + for (const RawEvent& event : events) { + if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) { + switch (event.type) { + case EventHubInterface::DEVICE_ADDED: + ALOGI("Device added: %i", event.deviceId); + break; + case EventHubInterface::DEVICE_REMOVED: + ALOGI("Device removed: %i", event.deviceId); + break; + case EventHubInterface::FINISHED_DEVICE_SCAN: + ALOGI("Finished device scan."); + break; + } + } else { + ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i", + event.deviceId, event.when, event.type, event.code, event.value); + } + } +} + +// --- EventHubTest --- +class EventHubTest : public testing::Test { +protected: + std::unique_ptr mEventHub; + // We are only going to emulate a single input device currently. + android::base::unique_fd mDeviceFd; + int32_t mDeviceId; + virtual void SetUp() override { + mEventHub = std::make_unique(); + consumeInitialDeviceAddedEvents(); + createDevice(); + mDeviceId = waitForDeviceCreation(); + } + virtual void TearDown() override { + mDeviceFd.reset(); + waitForDeviceClose(mDeviceId); + } + + void createDevice(); + /** + * Return the device id of the created device. + */ + int32_t waitForDeviceCreation(); + void waitForDeviceClose(int32_t deviceId); + void consumeInitialDeviceAddedEvents(); + void sendEvent(uint16_t type, uint16_t code, int32_t value); + std::vector getEvents(std::chrono::milliseconds timeout = 5ms); +}; + +std::vector EventHubTest::getEvents(std::chrono::milliseconds timeout) { + static constexpr size_t EVENT_BUFFER_SIZE = 256; + std::array eventBuffer; + std::vector events; + + while (true) { + size_t count = + mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size()); + if (count == 0) { + break; + } + events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count); + } + if (DEBUG) { + dumpEvents(events); + } + return events; +} + +void EventHubTest::createDevice() { + mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); + if (mDeviceFd < 0) { + FAIL() << "Can't open /dev/uinput :" << strerror(errno); + } + + /** + * Signal which type of events this input device supports. + * We will emulate a keyboard here. + */ + // enable key press/release event + if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); + } + + // enable set of KEY events + if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno); + } + + // enable synchronization event + if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); + } + + struct uinput_user_dev keyboard = {}; + strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE); + keyboard.id.bustype = BUS_USB; + keyboard.id.vendor = 0x01; + keyboard.id.product = 0x01; + keyboard.id.version = 1; + + if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) { + FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: " + << strerror(errno); + } + + if (ioctl(mDeviceFd, UI_DEV_CREATE)) { + FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno); + } +} + +/** + * Since the test runs on a real platform, there will be existing devices + * in addition to the test devices being added. Therefore, when EventHub is first created, + * it will return a lot of "device added" type of events. + */ +void EventHubTest::consumeInitialDeviceAddedEvents() { + std::vector events = getEvents(0ms); + std::set existingDevices; + // All of the events should be DEVICE_ADDED type, except the last one. + for (size_t i = 0; i < events.size() - 1; i++) { + const RawEvent& event = events[i]; + EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type); + existingDevices.insert(event.deviceId); + } + // None of the existing system devices should be changing while this test is run. + // Check that the returned device ids are unique for all of the existing devices. + EXPECT_EQ(existingDevices.size(), events.size() - 1); + // The last event should be "finished device scan" + EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type); +} + +int32_t EventHubTest::waitForDeviceCreation() { + // Wait a little longer than usual, to ensure input device has time to be created + std::vector events = getEvents(20ms); + EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void. + const RawEvent& deviceAddedEvent = events[0]; + EXPECT_EQ(static_cast(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type); + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId); + const int32_t deviceId = deviceAddedEvent.deviceId; + EXPECT_EQ(identifier.name, DEVICE_NAME); + const RawEvent& finishedDeviceScanEvent = events[1]; + EXPECT_EQ(static_cast(EventHubInterface::FINISHED_DEVICE_SCAN), + finishedDeviceScanEvent.type); + return deviceId; +} + +void EventHubTest::waitForDeviceClose(int32_t deviceId) { + std::vector events = getEvents(20ms); + ASSERT_EQ(2U, events.size()); + const RawEvent& deviceRemovedEvent = events[0]; + EXPECT_EQ(static_cast(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type); + EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId); + const RawEvent& finishedDeviceScanEvent = events[1]; + EXPECT_EQ(static_cast(EventHubInterface::FINISHED_DEVICE_SCAN), + finishedDeviceScanEvent.type); +} + +void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) { + struct input_event event = {}; + event.type = type; + event.code = code; + event.value = value; + event.time = {}; // uinput ignores the timestamp + + if (write(mDeviceFd, &event, sizeof(input_event)) < 0) { + std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16 + " with value %" PRId32 " : %s", + type, code, value, strerror(errno)); + ALOGE("%s", msg.c_str()); + ADD_FAILURE() << msg.c_str(); + } +} + +/** + * Ensure that input_events are generated with monotonic clock. + * That means input_event should receive a timestamp that is in the future of the time + * before the event was sent. + * Input system uses CLOCK_MONOTONIC everywhere in the code base. + */ +TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) { + nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + // key press + sendEvent(EV_KEY, KEY_HOME, 1); + sendEvent(EV_SYN, SYN_REPORT, 0); + + // key release + sendEvent(EV_KEY, KEY_HOME, 0); + sendEvent(EV_SYN, SYN_REPORT, 0); + + std::vector events = getEvents(); + ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events"; + for (const RawEvent& event : events) { + // Cannot use strict comparison because the events may happen too quickly + ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent"; + ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms) + << "Event times are too far apart"; + lastEventTime = event.when; // Ensure all returned events are monotonic + } +} -- cgit v1.2.3-59-g8ed1b From 81e2bb96bf4b7f3bfdcdcc33c9247e5b94748243 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 18 Dec 2019 15:03:51 -0800 Subject: Consume batches in dispatcher tests We are currently calling consume with consumeBatches=false. But that means we don't want to consume any batched events. As a result, when events are simply stored internally in inputconsumer, they are not returned to callers of consume(..). We never had tests that included MOVE events, and since DOWN events are never batched, we did not encounter this case. Now that we are adding a MOVE test, we need to ensure batches are consumed as well. Bug: none Test: atest inputflinger_tests Change-Id: Ieb13e1bb43bbf96f7f9352a283387ffdb11ba54a --- libs/input/InputTransport.cpp | 4 ++-- .../inputflinger/tests/InputDispatcher_test.cpp | 28 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 200e1f39d9..d53a557b28 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -301,8 +301,8 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (nWrite < 0) { int error = errno; #if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(), - msg->header.type, error); + ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(), + msg->header.type, strerror(error)); #endif if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 0701f3b3ee..994010ba0e 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -418,7 +418,7 @@ public: std::chrono::time_point start = std::chrono::steady_clock::now(); status_t status = WOULD_BLOCK; while (status == WOULD_BLOCK) { - status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq, + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; if (elapsed > 100ms) { @@ -932,6 +932,32 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT); } +TEST_F(InputDispatcherTest, TestMoveEvent) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + + mDispatcher->notifyMotion(&motionArgs); + // Window should receive motion down event. + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + motionArgs.action = AMOTION_EVENT_ACTION_MOVE; + motionArgs.sequenceNum += 1; + motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC); + motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + motionArgs.pointerCoords[0].getX() - 10); + + mDispatcher->notifyMotion(&motionArgs); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: -- cgit v1.2.3-59-g8ed1b From d078476b466d15691f508593a54611103e42c26e Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 1 Nov 2019 15:33:48 -0700 Subject: Add benchmarks for InputDispatcher No logs are generated when these benchmarks are ran. Bug: none Test: atest inputflinger_benchmarks Change-Id: Ifce2518657b2f266f8e8ccfaabac1331ecdf7935 --- services/inputflinger/benchmarks/Android.bp | 22 ++ .../benchmarks/InputDispatcher_benchmarks.cpp | 314 +++++++++++++++++++++ .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 3 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 services/inputflinger/benchmarks/Android.bp create mode 100644 services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp (limited to 'services/inputflinger') diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp new file mode 100644 index 0000000000..385b9816bd --- /dev/null +++ b/services/inputflinger/benchmarks/Android.bp @@ -0,0 +1,22 @@ +cc_benchmark { + name: "inputflinger_benchmarks", + srcs: [ + "InputDispatcher_benchmarks.cpp", + ], + defaults: ["inputflinger_defaults"], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libinput", + "libinputflinger_base", + "libinputreporter", + "liblog", + "libstatslog", + "libui", + "libutils", + ], + static_libs: [ + "libinputdispatcher", + ], +} diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp new file mode 100644 index 0000000000..9686ceaf2f --- /dev/null +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include "../dispatcher/InputDispatcher.h" + +namespace android::inputdispatcher { + +// 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; + +static const int32_t INJECT_EVENT_TIMEOUT = 5000; +static const int32_t DISPATCHING_TIMEOUT = 100000; + +static nsecs_t now() { + return systemTime(SYSTEM_TIME_MONOTONIC); +} + +// --- FakeInputDispatcherPolicy --- + +class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { +public: + FakeInputDispatcherPolicy() {} + +protected: + virtual ~FakeInputDispatcherPolicy() {} + +private: + virtual void notifyConfigurationChanged(nsecs_t) override {} + + virtual nsecs_t notifyANR(const sp&, const sp&, + const std::string& name) override { + ALOGE("The window is not responding : %s", name.c_str()); + return 0; + } + + virtual void notifyInputChannelBroken(const sp&) override {} + + virtual void notifyFocusChanged(const sp&, const sp&) override {} + + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { + *outConfig = mConfig; + } + + virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override { + return true; + } + + virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {} + + virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + + virtual nsecs_t interceptKeyBeforeDispatching(const sp&, const KeyEvent*, + uint32_t) override { + return 0; + } + + virtual bool dispatchUnhandledKey(const sp&, const KeyEvent*, uint32_t, + KeyEvent*) override { + return false; + } + + virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {} + + virtual void pokeUserActivity(nsecs_t, int32_t) override {} + + virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { + return false; + } + + virtual void onPointerDownOutsideFocus(const sp& newToken) override {} + + InputDispatcherConfiguration mConfig; +}; + +class FakeApplicationHandle : public InputApplicationHandle { +public: + FakeApplicationHandle() {} + virtual ~FakeApplicationHandle() {} + + virtual bool updateInfo() { + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + return true; + } +}; + +class FakeInputReceiver { +public: + void consumeEvent() { + uint32_t consumeSeq; + InputEvent* event; + + status_t result = WOULD_BLOCK; + while (result == WOULD_BLOCK) { + result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, + &event); + } + if (result != OK) { + ALOGE("Received result = %d from consume()", result); + } + result = mConsumer->sendFinishedSignal(consumeSeq, true); + if (result != OK) { + ALOGE("Received result = %d from sendFinishedSignal", result); + } + } + +protected: + explicit FakeInputReceiver(const sp& dispatcher, const std::string name) + : mDispatcher(dispatcher) { + InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); + mConsumer = std::make_unique(mClientChannel); + } + + virtual ~FakeInputReceiver() {} + + sp mDispatcher; + sp mServerChannel, mClientChannel; + std::unique_ptr mConsumer; + PreallocatedInputEventFactory mEventFactory; +}; + +class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver { +public: + static const int32_t WIDTH = 200; + static const int32_t HEIGHT = 200; + + FakeWindowHandle(const sp& inputApplicationHandle, + const sp& dispatcher, const std::string name) + : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) { + mDispatcher->registerInputChannel(mServerChannel); + + inputApplicationHandle->updateInfo(); + mInfo.applicationInfo = *inputApplicationHandle->getInfo(); + } + + virtual bool updateInfo() override { + mInfo.token = mServerChannel->getConnectionToken(); + mInfo.name = "FakeWindowHandle"; + mInfo.layoutParamsFlags = 0; + mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.frameLeft = mFrame.left; + mInfo.frameTop = mFrame.top; + mInfo.frameRight = mFrame.right; + mInfo.frameBottom = mFrame.bottom; + mInfo.globalScaleFactor = 1.0; + mInfo.touchableRegion.clear(); + mInfo.addTouchableRegion(mFrame); + mInfo.visible = true; + mInfo.canReceiveKeys = true; + mInfo.hasFocus = true; + mInfo.hasWallpaper = false; + mInfo.paused = false; + mInfo.layer = 0; + mInfo.ownerPid = INJECTOR_PID; + mInfo.ownerUid = INJECTOR_UID; + mInfo.inputFeatures = 0; + mInfo.displayId = ADISPLAY_ID_DEFAULT; + + return true; + } + +protected: + Rect mFrame; +}; + +static MotionEvent generateMotionEvent() { + PointerProperties pointerProperties[1]; + PointerCoords pointerCoords[1]; + + pointerProperties[0].clear(); + pointerProperties[0].id = 0; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100); + + const nsecs_t currentTime = now(); + + MotionEvent event; + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, + /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, + /*pointerCount*/ 1, pointerProperties, pointerCoords); + return event; +} + +static NotifyMotionArgs generateMotionArgs() { + PointerProperties pointerProperties[1]; + PointerCoords pointerCoords[1]; + + pointerProperties[0].clear(); + pointerProperties[0].id = 0; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100); + + const nsecs_t currentTime = now(); + // Define a valid motion event. + NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN, + /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + pointerProperties, pointerCoords, + /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); + + return args; +} + +static void benchmarkNotifyMotion(benchmark::State& state) { + // Create dispatcher + sp fakePolicy = new FakeInputDispatcherPolicy(); + sp dispatcher = new InputDispatcher(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + dispatcher->start(); + + // Create a window that will receive motion events + sp application = new FakeApplicationHandle(); + sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + + dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyMotionArgs motionArgs = generateMotionArgs(); + + for (auto _ : state) { + // Send ACTION_DOWN + motionArgs.action = AMOTION_EVENT_ACTION_DOWN; + motionArgs.sequenceNum = 0; + motionArgs.downTime = now(); + motionArgs.eventTime = motionArgs.downTime; + dispatcher->notifyMotion(&motionArgs); + + // Send ACTION_UP + motionArgs.action = AMOTION_EVENT_ACTION_UP; + motionArgs.sequenceNum = 1; + motionArgs.eventTime = now(); + dispatcher->notifyMotion(&motionArgs); + + window->consumeEvent(); + window->consumeEvent(); + } + + dispatcher->stop(); +} + +static void benchmarkInjectMotion(benchmark::State& state) { + // Create dispatcher + sp fakePolicy = new FakeInputDispatcherPolicy(); + sp dispatcher = new InputDispatcher(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + dispatcher->start(); + + // Create a window that will receive motion events + sp application = new FakeApplicationHandle(); + sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + + dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + MotionEvent event = generateMotionEvent(); + + for (auto _ : state) { + // Send ACTION_DOWN + event.setAction(AMOTION_EVENT_ACTION_DOWN); + event.setDownTime(now()); + dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + + // Send ACTION_UP + event.setAction(AMOTION_EVENT_ACTION_UP); + dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + + window->consumeEvent(); + window->consumeEvent(); + } + + dispatcher->stop(); +} + +BENCHMARK(benchmarkNotifyMotion); +BENCHMARK(benchmarkInjectMotion); + +} // namespace android::inputdispatcher + +BENCHMARK_MAIN(); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 994010ba0e..b7f7ac56e5 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -505,7 +505,7 @@ protected: const std::string name, int32_t displayId) : mDispatcher(dispatcher), mName(name), mDisplayId(displayId) { InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); - mConsumer = new InputConsumer(mClientChannel); + mConsumer = std::make_unique(mClientChannel); } virtual ~FakeInputReceiver() { @@ -518,7 +518,7 @@ protected: sp mDispatcher; sp mServerChannel, mClientChannel; - InputConsumer *mConsumer; + std::unique_ptr mConsumer; PreallocatedInputEventFactory mEventFactory; std::string mName; -- cgit v1.2.3-59-g8ed1b From d1c2318674b79af266e40e9f7c31e9ffea357b77 Mon Sep 17 00:00:00 2001 From: chaviw Date: Fri, 20 Dec 2019 18:44:56 -0800 Subject: Separated Receiver and Window for input tests This will allow for testing of multiple windows with the same receiver. Also added the ability to send a vector of points for touch events to prepare for tests with multiple points. Test: /data/nativetest64/inputflinger_tests/inputflinger_tests Bug: 140756730 Change-Id: I0c5d75ba1aa57233b4e82a4e97f2a661c8dff018 --- .../inputflinger/tests/InputDispatcher_test.cpp | 266 ++++++++++++--------- 1 file changed, 149 insertions(+), 117 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b7f7ac56e5..b4d7608770 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -21,6 +21,7 @@ #include #include +#include namespace android::inputdispatcher { @@ -37,6 +38,10 @@ static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; static const int32_t INJECTOR_PID = 999; static const int32_t INJECTOR_UID = 1001; +struct PointF { + float x; + float y; +}; // --- FakeInputDispatcherPolicy --- @@ -411,6 +416,11 @@ public: class FakeInputReceiver { public: + explicit FakeInputReceiver(const sp& clientChannel, const std::string name) + : mName(name) { + mConsumer = std::make_unique(clientChannel); + } + InputEvent* consume() { uint32_t consumeSeq; InputEvent* event; @@ -440,7 +450,7 @@ public: return nullptr; } - status = mConsumer->sendFinishedSignal(consumeSeq, handled()); + status = mConsumer->sendFinishedSignal(consumeSeq, true); if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } @@ -478,21 +488,6 @@ public: } } - void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, - expectedFlags); - } - - void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, - expectedFlags); - } - - void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, - expectedFlags); - } - void assertNoEvents() { InputEvent* event = consume(); ASSERT_EQ(nullptr, event) @@ -500,95 +495,105 @@ public: << ": should not have received any events, so consume() should return NULL"; } -protected: - explicit FakeInputReceiver(const sp& dispatcher, - const std::string name, int32_t displayId) : - mDispatcher(dispatcher), mName(name), mDisplayId(displayId) { - InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); - mConsumer = std::make_unique(mClientChannel); - } + sp getToken() { return mConsumer->getChannel()->getConnectionToken(); } - virtual ~FakeInputReceiver() { - } - - // return true if the event has been handled. - virtual bool handled() { - return false; - } - - sp mDispatcher; - sp mServerChannel, mClientChannel; - std::unique_ptr mConsumer; - PreallocatedInputEventFactory mEventFactory; +protected: + std::unique_ptr mConsumer; + PreallocatedInputEventFactory mEventFactory; - std::string mName; - int32_t mDisplayId; + std::string mName; }; -class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver { +class FakeWindowHandle : public InputWindowHandle { public: static const int32_t WIDTH = 600; static const int32_t HEIGHT = 800; + const std::string mName; FakeWindowHandle(const sp& inputApplicationHandle, - const sp& dispatcher, const std::string name, int32_t displayId) : - FakeInputReceiver(dispatcher, name, displayId), - mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) { - mDispatcher->registerInputChannel(mServerChannel); + const sp& dispatcher, const std::string name, + int32_t displayId, sp token = nullptr) + : mName(name) { + if (token == nullptr) { + sp serverChannel, clientChannel; + InputChannel::openInputChannelPair(name, serverChannel, clientChannel); + mInputReceiver = std::make_unique(clientChannel, name); + dispatcher->registerInputChannel(serverChannel); + token = serverChannel->getConnectionToken(); + } - inputApplicationHandle->updateInfo(); - mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - } + inputApplicationHandle->updateInfo(); + mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - virtual bool updateInfo() { - mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr; - mInfo.name = mName; - mInfo.layoutParamsFlags = mLayoutParamFlags; + mInfo.token = token; + mInfo.name = name; + mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; - mInfo.frameLeft = mFrame.left; - mInfo.frameTop = mFrame.top; - mInfo.frameRight = mFrame.right; - mInfo.frameBottom = mFrame.bottom; + mInfo.frameLeft = 0; + mInfo.frameTop = 0; + mInfo.frameRight = WIDTH; + mInfo.frameBottom = HEIGHT; mInfo.globalScaleFactor = 1.0; mInfo.touchableRegion.clear(); - mInfo.addTouchableRegion(mFrame); + mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); mInfo.visible = true; mInfo.canReceiveKeys = true; - mInfo.hasFocus = mFocused; + mInfo.hasFocus = false; mInfo.hasWallpaper = false; mInfo.paused = false; mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; - mInfo.displayId = mDisplayId; + mInfo.displayId = displayId; + } - return true; + virtual bool updateInfo() { return true; } + + void setFocus() { mInfo.hasFocus = true; } + + void setFrame(const Rect& frame) { + mInfo.frameLeft = frame.left; + mInfo.frameTop = frame.top; + mInfo.frameRight = frame.right; + mInfo.frameBottom = frame.bottom; + mInfo.touchableRegion.clear(); + mInfo.addTouchableRegion(frame); } - void setFocus() { - mFocused = true; + void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } + + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, + expectedFlags); } - void setFrame(const Rect& frame) { - mFrame.set(frame); + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, + expectedFlags); } - void setLayoutParamFlags(int32_t flags) { - mLayoutParamFlags = flags; + void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, + expectedFlags); } - void releaseChannel() { - mServerChannel.clear(); - InputWindowHandle::releaseChannel(); + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, + int32_t expectedFlags) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; + mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId, + expectedFlags); + } + + void assertNoEvents() { + ASSERT_NE(mInputReceiver, nullptr) + << "Call 'assertNoEvents' on a window with an InputReceiver"; + mInputReceiver->assertNoEvents(); } -protected: - virtual bool handled() override { return true; } - bool mFocused; - Rect mFrame; - int32_t mLayoutParamFlags; +private: + std::unique_ptr mInputReceiver; }; static int32_t injectKeyDown(const sp& dispatcher, @@ -659,31 +664,39 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA return args; } -static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) { - PointerProperties pointerProperties[1]; - PointerCoords pointerCoords[1]; +static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, + const std::vector& points) { + size_t pointerCount = points.size(); + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; - pointerProperties[0].clear(); - pointerProperties[0].id = 0; - pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200); + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y); + } nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords, - /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties, + pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); return args; } +static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) { + return generateMotionArgs(action, source, displayId, {PointF{100, 200}}); +} + TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", @@ -857,15 +870,37 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } -class FakeMonitorReceiver : public FakeInputReceiver, public RefBase { +class FakeMonitorReceiver { public: FakeMonitorReceiver(const sp& dispatcher, const std::string name, - int32_t displayId, bool isGestureMonitor = false) - : FakeInputReceiver(dispatcher, name, displayId) { - mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor); + int32_t displayId, bool isGestureMonitor = false) { + sp serverChannel, clientChannel; + InputChannel::openInputChannelPair(name, serverChannel, clientChannel); + mInputReceiver = std::make_unique(clientChannel, name); + dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor); } - sp getToken() { return mServerChannel->getConnectionToken(); } + sp getToken() { return mInputReceiver->getToken(); } + + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, + expectedDisplayId, expectedFlags); + } + + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, + expectedDisplayId, expectedFlags); + } + + void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, + expectedDisplayId, expectedFlags); + } + + void assertNoEvents() { mInputReceiver->assertNoEvents(); } + +private: + std::unique_ptr mInputReceiver; }; // Tests for gesture monitors @@ -875,15 +910,14 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); - sp monitor = - new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); } TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { @@ -896,14 +930,13 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); - sp monitor = - new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; window->consumeKeyDown(ADISPLAY_ID_DEFAULT); - monitor->assertNoEvents(); + monitor.assertNoEvents(); } TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) { @@ -912,24 +945,23 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); - sp monitor = - new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); window->releaseChannel(); - mDispatcher->pilferPointers(monitor->getToken()); + mDispatcher->pilferPointers(monitor.getToken()); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); } TEST_F(InputDispatcherTest, TestMoveEvent) { @@ -1047,28 +1079,28 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) // Test per-display input monitors for motion event. TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { - sp monitorInPrimary = - new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); - sp monitorInSecondary = - new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + FakeMonitorReceiver monitorInPrimary = + FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver monitorInSecondary = + FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); // Test touch down on primary display. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); - monitorInSecondary->assertNoEvents(); + monitorInSecondary.assertNoEvents(); // Test touch down on second display. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); - monitorInPrimary->assertNoEvents(); + monitorInPrimary.assertNoEvents(); windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); - monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); + monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID); // Test inject a non-pointer motion event. // If specific a display, it will dispatch to the focused window of particular display, @@ -1077,26 +1109,26 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); - monitorInPrimary->assertNoEvents(); + monitorInPrimary.assertNoEvents(); windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE); - monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE); + monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE); } // Test per-display input monitors for key event. TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { //Input monitor per display. - sp monitorInPrimary = - new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); - sp monitorInSecondary = - new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + FakeMonitorReceiver monitorInPrimary = + FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver monitorInSecondary = + FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); // Test inject a key down. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; windowInPrimary->assertNoEvents(); - monitorInPrimary->assertNoEvents(); + monitorInPrimary.assertNoEvents(); windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); - monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); + monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE); } class InputFilterTest : public InputDispatcherTest { -- cgit v1.2.3-59-g8ed1b From af87b3e09141558215e28f84d954590b5e7f2f16 Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 1 Oct 2019 16:59:28 -0700 Subject: Added input support for cloned layers This was done with a few changes: 1. Added a layerId in the input info so the InputInfo objects can be uniquely identified as per layer 2. When setting input info in InputDispatcher, compare InputInfo objects using layer id instead of input token. 3. Updated the crop region for layers based on the cloned hierarchy so the input is cropped to the correct region. Bug: 140756730 Test: InputDispatcherMultiWindowSameTokenTests Change-Id: I980f5d29d091fecb407f5cd6a289615505800927 --- include/input/InputWindow.h | 6 + libs/input/InputWindow.cpp | 2 + libs/input/tests/InputWindow_test.cpp | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 22 ++- .../inputflinger/tests/InputDispatcher_test.cpp | 149 +++++++++++++++++++++ services/surfaceflinger/BufferLayer.cpp | 15 ++- services/surfaceflinger/Layer.cpp | 63 ++++++--- services/surfaceflinger/Layer.h | 7 +- 8 files changed, 239 insertions(+), 27 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index f852cd0540..cbd64d520e 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -119,7 +119,11 @@ struct InputWindowInfo { /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ + // This value should NOT be used to uniquely identify the window. There may be different + // input windows that have the same token. sp token; + // This uniquely identifies the input window. + int32_t id = 0; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; @@ -203,6 +207,8 @@ public: sp getToken() const; + int32_t getId() const { return mInfo.id; } + sp getApplicationToken() { return mInfo.applicationInfo.token; } diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index ec28757933..74a05055eb 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -73,6 +73,7 @@ status_t InputWindowInfo::write(Parcel& output) const { status_t s = output.writeStrongBinder(token); if (s != OK) return s; + output.writeInt32(id); output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); @@ -116,6 +117,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { } ret.token = token; + ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index 6db18abacf..cdc81d2f04 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { sp touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); + i.id = 1; i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; @@ -72,6 +73,7 @@ TEST(InputWindowInfo, Parcelling) { p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5a49b5e16a..116625c03e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -240,6 +240,18 @@ static bool removeByValue(std::unordered_map& map, const V& value) { return removed; } +static bool haveSameToken(const sp& first, const sp& second) { + if (first == second) { + return true; + } + + if (first == nullptr || second == nullptr) { + return false; + } + + return first->getToken() == second->getToken(); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -3278,9 +3290,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. const std::vector>& oldHandles = getWindowHandlesLocked(displayId); - std::unordered_map, sp, IBinderHash> oldHandlesByTokens; + std::unordered_map> oldHandlesById; for (const sp& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; + oldHandlesById[handle->getId()] = handle; } std::vector> newHandles; @@ -3311,8 +3323,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp oldHandle = oldHandlesByTokens.at(handle->getToken()); + if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { + const sp& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { @@ -3370,7 +3382,7 @@ void InputDispatcher::setInputWindows(const std::vector>& sp oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - if (oldFocusedWindowHandle != newFocusedWindowHandle) { + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b4d7608770..c25122c72b 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -526,6 +526,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; + mInfo.id = 0; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -564,6 +565,13 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } + void setId(int32_t id) { mInfo.id = id; } + + void setWindowScale(float xScale, float yScale) { + mInfo.windowXScale = xScale; + mInfo.windowYScale = yScale; + } + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -586,12 +594,21 @@ public: expectedFlags); } + InputEvent* consume() { + if (mInputReceiver == nullptr) { + return nullptr; + } + return mInputReceiver->consume(); + } + void assertNoEvents() { ASSERT_NE(mInputReceiver, nullptr) << "Call 'assertNoEvents' on a window with an InputReceiver"; mInputReceiver->assertNoEvents(); } + sp getToken() { return mInfo.token; } + private: std::unique_ptr mInputReceiver; }; @@ -667,6 +684,10 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, const std::vector& points) { size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; @@ -1291,4 +1312,132 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownWasNotCalled(); } +// These tests ensures we can send touch events to a single client when there are multiple input +// windows that point to the same client token. +class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + sp application = new FakeApplicationHandle(); + mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", + ADISPLAY_ID_DEFAULT); + // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. + // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. + mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow1->setId(0); + mWindow1->setFrame(Rect(0, 0, 100, 100)); + + mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", + ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow2->setId(1); + mWindow2->setFrame(Rect(100, 100, 200, 200)); + + mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); + } + +protected: + sp mWindow1; + sp mWindow2; + + // Helper function to convert the point from screen coordinates into the window's space + static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { + float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); + float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); + return {x, y}; + } + + void consumeMotionEvent(const sp& window, int32_t expectedAction, + const std::vector& points) { + std::string name = window->mName; + InputEvent* event = window->consume(); + + ASSERT_NE(nullptr, event) << name.c_str() + << ": consumer should have returned non-NULL event."; + + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) + << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; + + const MotionEvent& motionEvent = static_cast(*event); + EXPECT_EQ(expectedAction, motionEvent.getAction()); + + for (size_t i = 0; i < points.size(); i++) { + float expectedX = points[i].x; + float expectedY = points[i].y; + + EXPECT_EQ(expectedX, motionEvent.getX(i)) + << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getX(i); + EXPECT_EQ(expectedY, motionEvent.getY(i)) + << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() + << ", got " << motionEvent.getY(i); + } + } +}; + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + PointF touchedPoint = {10, 10}; + PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + + // Release touch on Window 1 + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + // consume the UP event + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + + // Touch Window 2 + touchedPoint = {150, 150}; + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); +} + } // namespace android::inputdispatcher diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 054acc5069..bdecdb78d0 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -770,17 +770,20 @@ void BufferLayer::updateCloneBufferInfo() { // After buffer info is updated, the drawingState from the real layer needs to be copied into // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives. - // Therefore, temporarily store the relatives so they can be set in the cloned drawingState - // again. + // called. However, copying the drawingState would also overwrite the cloned layer's relatives + // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in + // the cloned drawingState again. wp tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; SortedVector> tmpZOrderRelatives = mDrawingState.zOrderRelatives; + wp tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; + InputWindowInfo tmpInputInfo = mDrawingState.inputInfo; + mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; + + mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; mDrawingState.zOrderRelatives = tmpZOrderRelatives; + mDrawingState.inputInfo = tmpInputInfo; } } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 35fc4be056..e7572f0054 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2025,6 +2025,7 @@ bool Layer::isRemovedFromCurrentState() const { InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; + info.id = sequence; if (info.displayId == ADISPLAY_ID_NONE) { info.displayId = getLayerStack(); @@ -2081,9 +2082,29 @@ InputWindowInfo Layer::fillInputInfo() { info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); } + // If the layer is a clone, we need to crop the input region to cloned root to prevent + // touches from going outside the cloned area. + if (isClone()) { + sp clonedRoot = getClonedRoot(); + if (clonedRoot != nullptr) { + Rect rect(clonedRoot->mScreenBounds); + info.touchableRegion = info.touchableRegion.intersect(rect); + } + } + return info; } +sp Layer::getClonedRoot() { + if (mClonedChild != nullptr) { + return this; + } + if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { + return nullptr; + } + return mDrawingParent.promote()->getClonedRoot(); +} + bool Layer::hasInput() const { return mDrawingState.inputInfo.token != nullptr; } @@ -2119,10 +2140,6 @@ void Layer::setInitialValuesForClone(const sp& clonedFrom) { // copy drawing state from cloned layer mDrawingState = clonedFrom->mDrawingState; mClonedFrom = clonedFrom; - - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; } void Layer::updateMirrorInfo() { @@ -2157,9 +2174,6 @@ void Layer::updateClonedDrawingState(std::map, sp>& clonedLayer if (isClonedFromAlive()) { sp clonedFrom = getClonedFrom(); mDrawingState = clonedFrom->mDrawingState; - // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple - // InputWindows per client token yet. - mDrawingState.inputInfo.token = nullptr; clonedLayersMap.emplace(clonedFrom, this); } @@ -2198,7 +2212,24 @@ void Layer::updateClonedChildren(const sp& mirrorRoot, } } -void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap) { +void Layer::updateClonedInputInfo(const std::map, sp>& clonedLayersMap) { + auto cropLayer = mDrawingState.touchableRegionCrop.promote(); + if (cropLayer != nullptr) { + if (clonedLayersMap.count(cropLayer) == 0) { + // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to + // self as crop layer to avoid going outside bounds. + mDrawingState.touchableRegionCrop = this; + } else { + const sp& clonedCropLayer = clonedLayersMap.at(cropLayer); + mDrawingState.touchableRegionCrop = clonedCropLayer; + } + } + // Cloned layers shouldn't handle watch outside since their z order is not determined by + // WM or the client. + mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH; +} + +void Layer::updateClonedRelatives(const std::map, sp>& clonedLayersMap) { mDrawingState.zOrderRelativeOf = nullptr; mDrawingState.zOrderRelatives.clear(); @@ -2206,11 +2237,11 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap return; } - sp clonedFrom = getClonedFrom(); + const sp& clonedFrom = getClonedFrom(); for (wp& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { - sp relative = relativeWeak.promote(); - auto clonedRelative = clonedLayersMap[relative]; - if (clonedRelative != nullptr) { + const sp& relative = relativeWeak.promote(); + if (clonedLayersMap.count(relative) > 0) { + auto& clonedRelative = clonedLayersMap.at(relative); mDrawingState.zOrderRelatives.add(clonedRelative); } } @@ -2220,12 +2251,14 @@ void Layer::updateClonedRelatives(std::map, sp> clonedLayersMap // In that case, we treat the layer as if the relativeOf has been removed. This way, it will // still traverse the children, but the layer with the missing relativeOf will not be shown // on screen. - sp relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); - sp clonedRelativeOf = clonedLayersMap[relativeOf]; - if (clonedRelativeOf != nullptr) { + const sp& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); + if (clonedLayersMap.count(relativeOf) > 0) { + const sp& clonedRelativeOf = clonedLayersMap.at(relativeOf); mDrawingState.zOrderRelativeOf = clonedRelativeOf; } + updateClonedInputInfo(clonedLayersMap); + for (sp& child : mDrawingChildren) { child->updateClonedRelatives(clonedLayersMap); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 843d3ae88e..d697a6a182 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -497,8 +497,9 @@ protected: void updateClonedDrawingState(std::map, sp>& clonedLayersMap); void updateClonedChildren(const sp& mirrorRoot, std::map, sp>& clonedLayersMap); - void updateClonedRelatives(std::map, sp> clonedLayersMap); + void updateClonedRelatives(const std::map, sp>& clonedLayersMap); void addChildToDrawing(const sp& layer); + void updateClonedInputInfo(const std::map, sp>& clonedLayersMap); public: /* @@ -972,6 +973,10 @@ private: // Returns true if the layer can draw shadows on its border. virtual bool canDrawShadows() const { return true; } + + // Find the root of the cloned hierarchy, this means the first non cloned parent. + // This will return null if first non cloned parent is not found. + sp getClonedRoot(); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From cb9232108322f4e74583feaf27c44c5331e4fa35 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 30 Dec 2019 14:05:11 -0800 Subject: Remove layer from window handle since it's no longer used Fixes: 146671630 Test: Builds and runs Change-Id: I095228a361ecdda8e1abd48c6d32b085b0dd20cf --- include/input/InputWindow.h | 1 - libs/input/InputWindow.cpp | 2 -- libs/input/tests/InputWindow_test.cpp | 2 -- .../benchmarks/InputDispatcher_benchmarks.cpp | 1 - services/inputflinger/dispatcher/InputDispatcher.cpp | 17 +++++++---------- services/inputflinger/tests/InputDispatcher_test.cpp | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index cbd64d520e..c44db514d2 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -161,7 +161,6 @@ struct InputWindowInfo { bool hasFocus; bool hasWallpaper; bool paused; - int32_t layer; int32_t ownerPid; int32_t ownerUid; int32_t inputFeatures; diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 74a05055eb..03ca459fb9 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -91,7 +91,6 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeBool(hasFocus); output.writeBool(hasWallpaper); output.writeBool(paused); - output.writeInt32(layer); output.writeInt32(ownerPid); output.writeInt32(ownerUid); output.writeInt32(inputFeatures); @@ -135,7 +134,6 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.hasFocus = from.readBool(); ret.hasWallpaper = from.readBool(); ret.paused = from.readBool(); - ret.layer = from.readInt32(); ret.ownerPid = from.readInt32(); ret.ownerUid = from.readInt32(); ret.inputFeatures = from.readInt32(); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index cdc81d2f04..d1cb527a57 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -58,7 +58,6 @@ TEST(InputWindowInfo, Parcelling) { i.hasFocus = false; i.hasWallpaper = false; i.paused = false; - i.layer = 7; i.ownerPid = 19; i.ownerUid = 24; i.inputFeatures = 29; @@ -91,7 +90,6 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.hasFocus, i2.hasFocus); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); - ASSERT_EQ(i.layer, i2.layer); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9686ceaf2f..0d3c821215 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -170,7 +170,6 @@ public: mInfo.hasFocus = true; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 116625c03e..4766bcefed 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3793,12 +3793,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " "portalToDisplayId=%d, paused=%s, hasFocus=%s, " - "hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, " - "type=0x%08x, layer=%d, " + "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, " + "flags=0x%08x, type=0x%08x, " "frame=[%d,%d][%d,%d], globalScale=%f, " - "windowScale=(%f,%f), " - "touchableRegion=", + "windowScale=(%f,%f), touchableRegion=", i, windowInfo->name.c_str(), windowInfo->displayId, windowInfo->portalToDisplayId, toString(windowInfo->paused), @@ -3807,11 +3805,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { toString(windowInfo->visible), toString(windowInfo->canReceiveKeys), windowInfo->layoutParamsFlags, - windowInfo->layoutParamsType, windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->globalScaleFactor, windowInfo->windowXScale, - windowInfo->windowYScale); + windowInfo->layoutParamsType, windowInfo->frameLeft, + windowInfo->frameTop, windowInfo->frameRight, + windowInfo->frameBottom, windowInfo->globalScaleFactor, + windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c25122c72b..5ffc89d210 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -543,7 +543,6 @@ public: mInfo.hasFocus = false; mInfo.hasWallpaper = false; mInfo.paused = false; - mInfo.layer = 0; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.inputFeatures = 0; -- cgit v1.2.3-59-g8ed1b From adfd4fa3745c9c414591bbd3141586e464a86750 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 20 Dec 2019 11:02:58 -0800 Subject: Update eventTime to prevent stale events Generate a new motionevent for each injection to ensure that eventtime remains up to date. In the InputDispatcher benchmarks, if the eventTime is not updated, then the test will be stuck, because dispatcher will just drop all events. To hit this condition, the benchmark must be ran for a sufficiently long amount of time so that isStaleEvent returns true. At that point, the benchmark for injectMotion will be stuck, because all events are dropped, and we cannot consume anything. The setDownTime api is unsupportedAppUsage in the Java layer, so we shouldn't remove it. Bug: none Test: flame:/data/benchmarktest64/inputflinger_benchmarks # ./inputflinger_benchmarks --benchmark_min_time=10 Change-Id: I1eb93ca9ddac52562e0da50de355d5cdc584c893 --- .../inputflinger/benchmarks/InputDispatcher_benchmarks.cpp | 11 +++++++---- services/inputflinger/dispatcher/InputDispatcher.cpp | 8 ++++---- services/inputflinger/dispatcher/InputDispatcher.h | 3 --- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 0d3c821215..246e735330 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -109,8 +109,14 @@ public: uint32_t consumeSeq; InputEvent* event; + std::chrono::time_point start = std::chrono::steady_clock::now(); status_t result = WOULD_BLOCK; while (result == WOULD_BLOCK) { + std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; + if (elapsed > 10ms) { + ALOGE("Waited too long for consumer to produce an event, giving up"); + break; + } result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); } @@ -282,12 +288,9 @@ static void benchmarkInjectMotion(benchmark::State& state) { dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); - MotionEvent event = generateMotionEvent(); - for (auto _ : state) { + MotionEvent event = generateMotionEvent(); // Send ACTION_DOWN - event.setAction(AMOTION_EVENT_ACTION_DOWN); - event.setDownTime(now()); dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4766bcefed..26c2d3f758 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -252,6 +252,10 @@ static bool haveSameToken(const sp& first, const spgetToken() == second->getToken(); } +static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { + return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -743,10 +747,6 @@ void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { #endif } -bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { - return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; -} - bool InputDispatcher::haveCommandsLocked() const { return !mCommandQueue.empty(); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 96a09e37df..f9eca01b2e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -168,9 +168,6 @@ private: bool isAppSwitchPendingLocked() REQUIRES(mLock); void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); - // Stale event latency optimization. - static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); - // Blocked event latency optimization. Drops old events when the user intends // to transfer focus to a new application. EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); -- cgit v1.2.3-59-g8ed1b From 5d22a2358e49e51782e980201e406fb4d592305a Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 11 Dec 2019 16:47:32 -0800 Subject: Handle different scale and offset for pointers in InputTarget. This change allows an InputTarget to have multiple pointers in different scale and offsets. This was done by using an array to store each pointerId to PointerInfo. PointerInfo contains the information that's unique to each pointer, scale and offset. Once the dispatcher is ready to queue the event, the dispatcher will request the InputTarget to gather all it's pointer information and normalize them to a single offset and scale. It will generate a new DispatcherEntry with a new MotionEvent that has all the coordinates in a single offset and scale. It will also set that offset and scale in the MotionEvent so it can be handled properly when sent to the client. Test: InputDispatcherMultiWindowSameTokenTests Test: manually with magnification Bug: 140756730 Change-Id: I449741a4b4b6fd7abc498d0a4b113da56fd42687 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 154 ++++++++++++++------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- services/inputflinger/dispatcher/InputTarget.cpp | 39 ++++++ services/inputflinger/dispatcher/InputTarget.h | 47 +++++-- .../inputflinger/tests/InputDispatcher_test.cpp | 103 ++++++++++++++ 5 files changed, 290 insertions(+), 59 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 26c2d3f758..0178811f0f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } +static std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, + EventEntry* eventEntry, + int32_t inputTargetFlags) { + if (inputTarget.useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); + return std::make_unique(eventEntry, // increments ref + inputTargetFlags, pointerInfo.xOffset, + pointerInfo.yOffset, inputTarget.globalScaleFactor, + pointerInfo.windowXScale, pointerInfo.windowYScale); + } + + ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); + const MotionEntry& motionEntry = static_cast(*eventEntry); + + PointerCoords pointerCoords[MAX_POINTERS]; + + // Use the first pointer information to normalize all other pointers. This could be any pointer + // as long as all other pointers are normalized to the same value and the final DispatchEntry + // uses the offset and scale for the normalized pointer. + const PointerInfo& firstPointerInfo = + inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; + + // Iterate through all pointers in the event to normalize against the first. + for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { + const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; + + // The scale factor is the ratio of the current pointers scale to the normalized scale. + float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; + float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; + + pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); + // First apply the current pointers offset to set the window at 0,0 + pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); + // Next scale the coordinates. + pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); + // Lastly, offset the coordinates so they're in the normalized pointer's frame. + pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, + -firstPointerInfo.yOffset); + } + + MotionEntry* combinedMotionEntry = + new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, + motionEntry.action, motionEntry.actionButton, motionEntry.flags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.pointerCount, + motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, + 0 /* yOffset */); + + return std::make_unique(combinedMotionEntry, // increments ref + inputTargetFlags, firstPointerInfo.xOffset, + firstPointerInfo.yOffset, inputTarget.globalScaleFactor, + firstPointerInfo.windowXScale, + firstPointerInfo.windowYScale); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1111,7 +1172,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event sp connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { if (DEBUG_FOCUS) { ALOGD("Dropping event delivery to target with channel '%s' because it " @@ -1785,23 +1846,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } + std::vector::iterator it = + std::find_if(inputTargets.begin(), inputTargets.end(), + [&windowHandle](const InputTarget& inputTarget) { + return inputTarget.inputChannel->getConnectionToken() == + windowHandle->getToken(); + }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget target; - target.inputChannel = inputChannel; - target.flags = targetFlags; - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - target.pointerIds = pointerIds; - inputTargets.push_back(target); + + if (it == inputTargets.end()) { + InputTarget inputTarget; + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + inputTarget.inputChannel = inputChannel; + inputTarget.flags = targetFlags; + inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTargets.push_back(inputTarget); + it = inputTargets.end() - 1; + } + + ALOG_ASSERT(it->flags == targetFlags); + ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); + + it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, @@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = xOffset; - target.yOffset = yOffset; - target.pointerIds.clear(); - target.globalScaleFactor = 1.0f; + target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } @@ -2045,7 +2114,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget) { + const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", @@ -2056,9 +2125,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, - inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale, inputTarget->pointerIds.value); + connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, + inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, + inputTarget.windowYScale, inputTarget.pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -2072,13 +2141,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (inputTarget->flags & InputTarget::FLAG_SPLIT) { + if (inputTarget.flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { + if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = - splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); + splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); if (!splitMotionEntry) { return; // split event was dropped } @@ -2100,7 +2169,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget) { + const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 @@ -2133,7 +2202,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget, + const InputTarget& inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", @@ -2141,7 +2210,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } - int32_t inputTargetFlags = inputTarget->flags; + int32_t inputTargetFlags = inputTarget.flags; if (!(inputTargetFlags & dispatchMode)) { return; } @@ -2149,11 +2218,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = - new DispatchEntry(eventEntry, // increments ref - inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale); + std::unique_ptr dispatchEntry = + createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); // Apply target flags and update the connection's input state. switch (eventEntry->type) { @@ -2168,7 +2234,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; return; // skip the inconsistent event } break; @@ -2215,12 +2280,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio "event", connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; return; // skip the inconsistent event } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, - inputTarget->inputChannel->getConnectionToken()); + inputTarget.inputChannel->getConnectionToken()); break; } @@ -2238,7 +2302,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } // Enqueue the dispatch entry. - connection->outboundQueue.push_back(dispatchEntry); + connection->outboundQueue.push_back(dispatchEntry.release()); traceOutboundQueueLength(connection); } @@ -2611,21 +2675,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - } else { - target.xOffset = 0; - target.yOffset = 0; - target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - &target, InputTarget::FLAG_DISPATCH_AS_IS); + target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index f9eca01b2e..50b52502a0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -378,13 +378,13 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) + EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) + EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget, int32_t dispatchMode) + const InputTarget& inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 80fa2cb995..bd7cb3ed9b 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,4 +42,43 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } +void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, + float windowXScale, float windowYScale) { + // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors + // and non splittable windows since we will just use all the pointers from the input event. + if (newPointerIds.isEmpty()) { + setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); + return; + } + + // Ensure that the new set of pointers doesn't overlap with the current set of pointers. + ALOG_ASSERT((pointerIds & newPointerIds) == 0); + + pointerIds |= newPointerIds; + while (!newPointerIds.isEmpty()) { + int32_t pointerId = newPointerIds.clearFirstMarkedBit(); + pointerInfos[pointerId].xOffset = xOffset; + pointerInfos[pointerId].yOffset = yOffset; + pointerInfos[pointerId].windowXScale = windowXScale; + pointerInfos[pointerId].windowYScale = windowYScale; + } +} + +void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale) { + pointerIds.clear(); + pointerInfos[0].xOffset = xOffset; + pointerInfos[0].yOffset = yOffset; + pointerInfos[0].windowXScale = windowXScale; + pointerInfos[0].windowYScale = windowYScale; +} + +bool InputTarget::useDefaultPointerInfo() const { + return pointerIds.isEmpty(); +} + +const PointerInfo& InputTarget::getDefaultPointerInfo() const { + return pointerInfos[0]; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 2e9bca251f..fd98df1782 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -23,6 +23,22 @@ namespace android::inputdispatcher { +/* + * Information about each pointer for an InputTarget. This includes offset and scale so + * all pointers can be normalized to a single offset and scale. + */ +struct PointerInfo { + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset = 0.0f; + float yOffset = 0.0f; + + // Scaling factor to apply to MotionEvent as it is delivered. + // (ignored for KeyEvents) + float windowXScale = 1.0f; + float windowYScale = 1.0f; +}; + /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to @@ -95,20 +111,35 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset = 0.0f; - float yOffset = 0.0f; - // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - float windowXScale = 1.0f; - float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. - BitSet32 pointerIds{}; + BitSet32 pointerIds; + // The data is stored by the pointerId. Use the marked bits in pointerIds to look up PointerInfo + // per pointerId. + PointerInfo pointerInfos[MAX_POINTERS]; + + void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, + float windowYScale); + void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale); + + /** + * Returns whether the default pointer information should be used. This will be true when the + * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors + * and non splittable windows since we want all pointers for the EventEntry to go to this + * target. + */ + bool useDefaultPointerInfo() const; + + /** + * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is + * true. + */ + const PointerInfo& getDefaultPointerInfo() const; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5ffc89d210..bd8d2a4d39 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { + mWindow1->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From b38d8c6ebc73eef1b897d4019ce5fdab8539ba13 Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Wed, 8 Jan 2020 21:20:39 +0000 Subject: Revert "Handle different scale and offset for pointers in InputTarget." This reverts commit 5d22a2358e49e51782e980201e406fb4d592305a. Reason for revert: b/147371357 Change-Id: I1987192eba55424265a9910237a2df96c2336807 Bug: 147371357 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 154 +++++++-------------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- services/inputflinger/dispatcher/InputTarget.cpp | 39 ------ services/inputflinger/dispatcher/InputTarget.h | 47 ++----- .../inputflinger/tests/InputDispatcher_test.cpp | 103 -------------- 5 files changed, 59 insertions(+), 290 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 0178811f0f..26c2d3f758 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,67 +256,6 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } -static std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, - EventEntry* eventEntry, - int32_t inputTargetFlags) { - if (inputTarget.useDefaultPointerInfo()) { - const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); - return std::make_unique(eventEntry, // increments ref - inputTargetFlags, pointerInfo.xOffset, - pointerInfo.yOffset, inputTarget.globalScaleFactor, - pointerInfo.windowXScale, pointerInfo.windowYScale); - } - - ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); - const MotionEntry& motionEntry = static_cast(*eventEntry); - - PointerCoords pointerCoords[MAX_POINTERS]; - - // Use the first pointer information to normalize all other pointers. This could be any pointer - // as long as all other pointers are normalized to the same value and the final DispatchEntry - // uses the offset and scale for the normalized pointer. - const PointerInfo& firstPointerInfo = - inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; - - // Iterate through all pointers in the event to normalize against the first. - for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { - const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; - - // The scale factor is the ratio of the current pointers scale to the normalized scale. - float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; - float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; - - pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); - // First apply the current pointers offset to set the window at 0,0 - pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); - // Next scale the coordinates. - pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); - // Lastly, offset the coordinates so they're in the normalized pointer's frame. - pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, - -firstPointerInfo.yOffset); - } - - MotionEntry* combinedMotionEntry = - new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, - motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, - motionEntry.action, motionEntry.actionButton, motionEntry.flags, - motionEntry.metaState, motionEntry.buttonState, - motionEntry.classification, motionEntry.edgeFlags, - motionEntry.xPrecision, motionEntry.yPrecision, - motionEntry.xCursorPosition, motionEntry.yCursorPosition, - motionEntry.downTime, motionEntry.pointerCount, - motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, - 0 /* yOffset */); - - return std::make_unique(combinedMotionEntry, // increments ref - inputTargetFlags, firstPointerInfo.xOffset, - firstPointerInfo.yOffset, inputTarget.globalScaleFactor, - firstPointerInfo.windowXScale, - firstPointerInfo.windowYScale); -} - // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1172,7 +1111,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event sp connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { if (DEBUG_FOCUS) { ALOGD("Dropping event delivery to target with channel '%s' because it " @@ -1846,34 +1785,23 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - std::vector::iterator it = - std::find_if(inputTargets.begin(), inputTargets.end(), - [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); - }); - - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - - if (it == inputTargets.end()) { - InputTarget inputTarget; - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } - inputTarget.inputChannel = inputChannel; - inputTarget.flags = targetFlags; - inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; - inputTargets.push_back(inputTarget); - it = inputTargets.end() - 1; + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; } - ALOG_ASSERT(it->flags == targetFlags); - ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); - - it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + InputTarget target; + target.inputChannel = inputChannel; + target.flags = targetFlags; + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; + target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + target.pointerIds = pointerIds; + inputTargets.push_back(target); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, @@ -1896,7 +1824,10 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); + target.xOffset = xOffset; + target.yOffset = yOffset; + target.pointerIds.clear(); + target.globalScaleFactor = 1.0f; inputTargets.push_back(target); } @@ -2114,7 +2045,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget& inputTarget) { + const InputTarget* inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", @@ -2125,9 +2056,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, - inputTarget.windowYScale, inputTarget.pointerIds.value); + connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, + inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale, inputTarget->pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -2141,13 +2072,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (inputTarget.flags & InputTarget::FLAG_SPLIT) { + if (inputTarget->flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); - if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { + if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = - splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); + splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); if (!splitMotionEntry) { return; // split event was dropped } @@ -2169,7 +2100,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget& inputTarget) { + const InputTarget* inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 @@ -2202,7 +2133,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget& inputTarget, + const InputTarget* inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", @@ -2210,7 +2141,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } - int32_t inputTargetFlags = inputTarget.flags; + int32_t inputTargetFlags = inputTarget->flags; if (!(inputTargetFlags & dispatchMode)) { return; } @@ -2218,8 +2149,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - std::unique_ptr dispatchEntry = - createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); + DispatchEntry* dispatchEntry = + new DispatchEntry(eventEntry, // increments ref + inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale); // Apply target flags and update the connection's input state. switch (eventEntry->type) { @@ -2234,6 +2168,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName().c_str()); #endif + delete dispatchEntry; return; // skip the inconsistent event } break; @@ -2280,11 +2215,12 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio "event", connection->getInputChannelName().c_str()); #endif + delete dispatchEntry; return; // skip the inconsistent event } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, - inputTarget.inputChannel->getConnectionToken()); + inputTarget->inputChannel->getConnectionToken()); break; } @@ -2302,7 +2238,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } // Enqueue the dispatch entry. - connection->outboundQueue.push_back(dispatchEntry.release()); + connection->outboundQueue.push_back(dispatchEntry); traceOutboundQueueLength(connection); } @@ -2675,15 +2611,21 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + } else { + target.xOffset = 0; + target.yOffset = 0; + target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - target, InputTarget::FLAG_DISPATCH_AS_IS); + &target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 50b52502a0..f9eca01b2e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -378,13 +378,13 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget& inputTarget) + EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget& inputTarget) + EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget& inputTarget, int32_t dispatchMode) + const InputTarget* inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index bd7cb3ed9b..80fa2cb995 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,43 +42,4 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } -void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, - float windowXScale, float windowYScale) { - // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors - // and non splittable windows since we will just use all the pointers from the input event. - if (newPointerIds.isEmpty()) { - setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); - return; - } - - // Ensure that the new set of pointers doesn't overlap with the current set of pointers. - ALOG_ASSERT((pointerIds & newPointerIds) == 0); - - pointerIds |= newPointerIds; - while (!newPointerIds.isEmpty()) { - int32_t pointerId = newPointerIds.clearFirstMarkedBit(); - pointerInfos[pointerId].xOffset = xOffset; - pointerInfos[pointerId].yOffset = yOffset; - pointerInfos[pointerId].windowXScale = windowXScale; - pointerInfos[pointerId].windowYScale = windowYScale; - } -} - -void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale) { - pointerIds.clear(); - pointerInfos[0].xOffset = xOffset; - pointerInfos[0].yOffset = yOffset; - pointerInfos[0].windowXScale = windowXScale; - pointerInfos[0].windowYScale = windowYScale; -} - -bool InputTarget::useDefaultPointerInfo() const { - return pointerIds.isEmpty(); -} - -const PointerInfo& InputTarget::getDefaultPointerInfo() const { - return pointerInfos[0]; -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index fd98df1782..2e9bca251f 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -23,22 +23,6 @@ namespace android::inputdispatcher { -/* - * Information about each pointer for an InputTarget. This includes offset and scale so - * all pointers can be normalized to a single offset and scale. - */ -struct PointerInfo { - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset = 0.0f; - float yOffset = 0.0f; - - // Scaling factor to apply to MotionEvent as it is delivered. - // (ignored for KeyEvents) - float windowXScale = 1.0f; - float windowYScale = 1.0f; -}; - /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to @@ -111,35 +95,20 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset = 0.0f; + float yOffset = 0.0f; + // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; + float windowXScale = 1.0f; + float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. - BitSet32 pointerIds; - // The data is stored by the pointerId. Use the marked bits in pointerIds to look up PointerInfo - // per pointerId. - PointerInfo pointerInfos[MAX_POINTERS]; - - void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, - float windowYScale); - void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale); - - /** - * Returns whether the default pointer information should be used. This will be true when the - * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors - * and non splittable windows since we want all pointers for the EventEntry to go to this - * target. - */ - bool useDefaultPointerInfo() const; - - /** - * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is - * true. - */ - const PointerInfo& getDefaultPointerInfo() const; + BitSet32 pointerIds{}; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index bd8d2a4d39..5ffc89d210 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1439,107 +1439,4 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { - mWindow2->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); -} - -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { - mWindow2->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); - - // Move both windows - touchedPoints = {{20, 20}, {175, 175}}; - expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), - getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); -} - -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { - mWindow1->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); - - // Move both windows - touchedPoints = {{20, 20}, {175, 175}}; - expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), - getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); -} - } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 5d6b6617e16eafd19fc1d003924d948def91ef1f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 8 Jan 2020 16:03:04 -0800 Subject: Use const reference instead of pointer Improve dispatcher api's by using const reference instead of pointer. This would highlight the fact that the object is not nullable. Bug: 140756730 Test: presubmit Change-Id: I54d600815b973c6172b60d8f7a3ce4a12175b82e --- .../inputflinger/dispatcher/InputDispatcher.cpp | 44 +++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +-- services/inputflinger/dispatcher/InputTarget.h | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 26c2d3f758..a3cb4f8f68 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1111,7 +1111,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event sp connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { if (DEBUG_FOCUS) { ALOGD("Dropping event delivery to target with channel '%s' because it " @@ -2045,7 +2045,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget) { + const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", @@ -2056,9 +2056,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, - inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale, inputTarget->pointerIds.value); + connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, + inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, + inputTarget.windowYScale, inputTarget.pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -2072,13 +2072,15 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (inputTarget->flags & InputTarget::FLAG_SPLIT) { - ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); + if (inputTarget.flags & InputTarget::FLAG_SPLIT) { + LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, + "Entry type %s should not have FLAG_SPLIT", + EventEntry::typeToString(eventEntry->type)); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { + if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = - splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); + splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); if (!splitMotionEntry) { return; // split event was dropped } @@ -2100,7 +2102,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget) { + const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 @@ -2133,7 +2135,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget, + const InputTarget& inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", @@ -2141,7 +2143,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } - int32_t inputTargetFlags = inputTarget->flags; + int32_t inputTargetFlags = inputTarget.flags; if (!(inputTargetFlags & dispatchMode)) { return; } @@ -2149,11 +2151,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = - new DispatchEntry(eventEntry, // increments ref - inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale); + std::unique_ptr dispatchEntry = + std::make_unique(eventEntry, // increments ref + inputTargetFlags, inputTarget.xOffset, + inputTarget.yOffset, inputTarget.globalScaleFactor, + inputTarget.windowXScale, inputTarget.windowYScale); // Apply target flags and update the connection's input state. switch (eventEntry->type) { @@ -2168,7 +2170,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; return; // skip the inconsistent event } break; @@ -2215,12 +2216,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio "event", connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; return; // skip the inconsistent event } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, - inputTarget->inputChannel->getConnectionToken()); + inputTarget.inputChannel->getConnectionToken()); break; } @@ -2238,7 +2238,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } // Enqueue the dispatch entry. - connection->outboundQueue.push_back(dispatchEntry); + connection->outboundQueue.push_back(dispatchEntry.release()); traceOutboundQueueLength(connection); } @@ -2625,7 +2625,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - &target, InputTarget::FLAG_DISPATCH_AS_IS); + target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index f9eca01b2e..50b52502a0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -378,13 +378,13 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) + EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) + EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp& connection, EventEntry* eventEntry, - const InputTarget* inputTarget, int32_t dispatchMode) + const InputTarget& inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 2e9bca251f..1ba5effada 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -108,7 +108,7 @@ struct InputTarget { // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. - BitSet32 pointerIds{}; + BitSet32 pointerIds; }; std::string dispatchModeToString(int32_t dispatchMode); -- cgit v1.2.3-59-g8ed1b From f1035d4f7da852c63cb91ebaed863836b6a23f07 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 20 Sep 2019 16:32:01 +0100 Subject: Move focus dispatch to input (1/2) Input now tells the apps when they have focus. When a focus change has been detected, we enqueue a FocusEntry into the dispatcher queue. It will be sent to the app via the socket. At the same time, we tell the apps the current state of the touch mode. Bug: 70668286 Test: atest libinput_tests inputflinger_tests Change-Id: Icefb9383ba8424162d739c9e981bd1dd01cc7941 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 68 ++++++++- services/inputflinger/dispatcher/Entry.cpp | 15 ++ services/inputflinger/dispatcher/Entry.h | 21 ++- .../inputflinger/dispatcher/InputDispatcher.cpp | 54 ++++++- services/inputflinger/dispatcher/InputDispatcher.h | 4 + .../inputflinger/tests/InputDispatcher_test.cpp | 162 +++++++++++++++++++-- 6 files changed, 300 insertions(+), 24 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 8d36ba7b70..04749e6d13 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,22 +112,31 @@ public: if (consumed != OK) { return nullptr; } - mInputConsumer->sendFinishedSignal(seqId, true); + status_t status = mInputConsumer->sendFinishedSignal(seqId, true); + EXPECT_EQ(OK, status) << "Could not send finished signal"; return ev; } + void assertFocusChange(bool hasFocus) { + InputEvent *ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, ev->getType()); + FocusEvent *focusEvent = static_cast(ev); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + } + void expectTap(int x, int y) { InputEvent* ev = consumeEvent(); - EXPECT_TRUE(ev != nullptr); - EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); MotionEvent* mev = static_cast(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); EXPECT_EQ(x, mev->getX(0)); EXPECT_EQ(y, mev->getY(0)); ev = consumeEvent(); - EXPECT_TRUE(ev != nullptr); - EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); mev = static_cast(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } @@ -212,7 +221,7 @@ public: ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); const auto display = mComposerClient->getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); + ASSERT_NE(display, nullptr); DisplayInfo info; ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info)); @@ -259,18 +268,28 @@ void injectTap(int x, int y) { TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); - EXPECT_TRUE(surface->consumeEvent() != nullptr); + EXPECT_NE(surface->consumeEvent(), nullptr); } +/** + * Set up two surfaces side-by-side. Tap each surface. + * Next, swap the positions of the two surfaces. Inject tap into the two + * original locations. Ensure that the tap is received by the surfaces in the + * reverse order. + */ TEST_F(InputSurfacesTest, input_respects_positioning) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(100, 100); + surface->assertFocusChange(true); std::unique_ptr surface2 = makeSurface(100, 100); surface2->showAt(200, 200); + surface->assertFocusChange(false); + surface2->assertFocusChange(true); injectTap(201, 201); surface2->expectTap(1, 1); @@ -297,11 +316,16 @@ TEST_F(InputSurfacesTest, input_respects_layering) { std::unique_ptr surface2 = makeSurface(100, 100); surface->showAt(10, 10); + surface->assertFocusChange(true); surface2->showAt(10, 10); + surface->assertFocusChange(false); + surface2->assertFocusChange(true); surface->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); + surface2->assertFocusChange(false); + surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -309,6 +333,8 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); + surface2->assertFocusChange(true); + surface->assertFocusChange(false); injectTap(11, 11); surface2->expectTap(1, 1); @@ -316,6 +342,8 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + surface2->assertFocusChange(false); + surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -328,9 +356,12 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr bgSurface = makeSurface(100, 100); std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); + bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); + fgSurface->assertFocusChange(true); + bgSurface->assertFocusChange(false); injectTap(106, 106); fgSurface->expectTap(1, 1); @@ -344,9 +375,12 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr parentSurface = makeSurface(100, 100); std::unique_ptr childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); + parentSurface->assertFocusChange(true); childSurface->mInputInfo.surfaceInset = 10; childSurface->showAt(100, 100); + childSurface->assertFocusChange(true); + parentSurface->assertFocusChange(false); childSurface->doTransaction([&](auto &t, auto &sc) { t.setPosition(sc, -5, -5); @@ -365,9 +399,12 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr bgSurface = makeSurface(100, 100); std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); + bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); + bgSurface->assertFocusChange(false); + fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -384,6 +421,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); + fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -400,6 +438,7 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -414,7 +453,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + bufferSurface->assertFocusChange(true); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -431,7 +473,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { postBuffer(bufferSurface->mSurfaceControl); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); + bufferSurface->assertFocusChange(true); + bgSurface->assertFocusChange(false); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -447,7 +492,10 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); fgSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + fgSurface->assertFocusChange(true); injectTap(11, 11); fgSurface->expectTap(1, 1); @@ -464,12 +512,17 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); containerSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + containerSurface->assertFocusChange(true); injectTap(11, 11); containerSurface->expectTap(1, 1); containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + containerSurface->assertFocusChange(false); + bgSurface->assertFocusChange(true); injectTap(11, 11); bgSurface->expectTap(1, 1); @@ -478,6 +531,7 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr surface = makeSurface(100, 100); surface->showAt(-1, -1); + surface->assertFocusChange(true); injectTap(0, 0); surface->expectTap(1, 1); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index e925f5b6ba..b7236547de 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -111,6 +111,21 @@ void DeviceResetEntry::appendDescription(std::string& msg) const { msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); } +// --- FocusEntry --- + +// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries +FocusEntry::FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp connectionToken, + bool hasFocus) + : EventEntry(sequenceNum, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER), + connectionToken(connectionToken), + hasFocus(hasFocus) {} + +FocusEntry::~FocusEntry() {} + +void FocusEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); +} + // --- KeyEntry --- KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 9dcaadc6ee..e8c37f0e6e 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -33,7 +33,13 @@ namespace android::inputdispatcher { constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; struct EventEntry { - enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION }; + enum class Type { + CONFIGURATION_CHANGED, + DEVICE_RESET, + FOCUS, + KEY, + MOTION, + }; static const char* typeToString(Type type) { switch (type) { @@ -41,6 +47,8 @@ struct EventEntry { return "CONFIGURATION_CHANGED"; case Type::DEVICE_RESET: return "DEVICE_RESET"; + case Type::FOCUS: + return "FOCUS"; case Type::KEY: return "KEY"; case Type::MOTION: @@ -102,6 +110,17 @@ protected: virtual ~DeviceResetEntry(); }; +struct FocusEntry : EventEntry { + sp connectionToken; + bool hasFocus; + + FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp connectionToken, bool hasFocus); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~FocusEntry(); +}; + struct KeyEntry : EventEntry { int32_t deviceId; uint32_t source; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a3cb4f8f68..ce7399ef6e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -469,6 +469,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } + case EventEntry::Type::FOCUS: { + FocusEntry* typedEntry = static_cast(mPendingEvent); + dispatchFocusLocked(currentTime, typedEntry); + done = true; + dropReason = DropReason::NOT_DROPPED; // focus events are never dropped + break; + } + case EventEntry::Type::KEY: { KeyEntry* typedEntry = static_cast(mPendingEvent); if (isAppSwitchDue) { @@ -573,7 +581,8 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { break; } case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { + case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::FOCUS: { // nothing to do break; } @@ -712,6 +721,7 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type)); @@ -872,6 +882,25 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset return true; } +void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { + FocusEntry* focusEntry = + new FocusEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, now(), window.getToken(), hasFocus); + enqueueInboundEventLocked(focusEntry); +} + +void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) { + sp channel = getInputChannelLocked(entry->connectionToken); + if (channel == nullptr) { + return; // Window has gone away + } + InputTarget target; + target.inputChannel = channel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + entry->dispatchInProgress = true; + + dispatchEventLocked(currentTime, entry, {target}); +} + bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. @@ -1254,6 +1283,7 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { displayId = motionEntry.displayId; break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type)); @@ -1993,6 +2023,10 @@ std::string InputDispatcher::getApplicationWindowLabel( } void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { + if (eventEntry.type == EventEntry::Type::FOCUS) { + // Focus events are passed to apps, but do not represent user activity. + return; + } int32_t displayId = getTargetDisplayId(eventEntry); sp focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); @@ -2027,6 +2061,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { eventType = USER_ACTIVITY_EVENT_BUTTON; break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events are not user activity", @@ -2224,6 +2259,9 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio break; } + case EventEntry::Type::FOCUS: { + break; + } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", @@ -2359,6 +2397,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, reportTouchEventForStatistics(*motionEntry); break; } + case EventEntry::Type::FOCUS: { + FocusEntry* focusEntry = static_cast(eventEntry); + status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, + focusEntry->hasFocus, + mInTouchMode); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", @@ -2598,6 +2644,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( *cancelationEventEntry)); break; } + case EventEntry::Type::FOCUS: { + LOG_ALWAYS_FATAL("Canceling focus events is not supported"); + break; + } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", @@ -3394,6 +3444,7 @@ void InputDispatcher::setInputWindows(const std::vector>& CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } mFocusedWindowHandlesByDisplay.erase(displayId); } @@ -3403,6 +3454,7 @@ void InputDispatcher::setInputWindows(const std::vector>& newFocusedWindowHandle->getName().c_str(), displayId); } mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } if (mFocusedDisplayId == displayId) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 50b52502a0..a4ba0dec6a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -157,6 +157,9 @@ private: // Cleans up input state when dropping an inbound event. void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock); + // Enqueues a focus event. + void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock); + // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); @@ -299,6 +302,7 @@ private: nsecs_t* nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock); void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, const std::vector& inputTargets) REQUIRES(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5ffc89d210..c262abb229 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -482,12 +482,31 @@ public: EXPECT_EQ(expectedFlags, motionEvent.getFlags()); break; } + case AINPUT_EVENT_TYPE_FOCUS: { + FAIL() << "Use 'consumeFocusEvent' for FOCUS events"; + } default: { FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; } } } + void consumeFocusEvent(bool hasFocus, bool inTouchMode) { + InputEvent* event = consume(); + ASSERT_NE(nullptr, event) << mName.c_str() + << ": consumer should have returned non-NULL event."; + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType()) + << "Got " << inputEventTypeToString(event->getType()) + << " event instead of FOCUS event"; + + ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + FocusEvent* focusEvent = static_cast(event); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); + } + void assertNoEvents() { InputEvent* event = consume(); ASSERT_EQ(nullptr, event) @@ -508,7 +527,6 @@ class FakeWindowHandle : public InputWindowHandle { public: static const int32_t WIDTH = 600; static const int32_t HEIGHT = 800; - const std::string mName; FakeWindowHandle(const sp& inputApplicationHandle, const sp& dispatcher, const std::string name, @@ -551,7 +569,7 @@ public: virtual bool updateInfo() { return true; } - void setFocus() { mInfo.hasFocus = true; } + void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; @@ -586,6 +604,12 @@ public: expectedFlags); } + void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { + ASSERT_NE(mInputReceiver, nullptr) + << "Cannot consume events from a window with no receiver"; + mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode); + } + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, int32_t expectedFlags) { ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; @@ -608,7 +632,10 @@ public: sp getToken() { return mInfo.token; } + const std::string& getName() { return mName; } + private: + const std::string mName; std::unique_ptr mInputReceiver; }; @@ -759,10 +786,11 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - // Expect one focus window exist in display. - windowSecond->setFocus(); - + // Display should have only one focused window + windowSecond->setFocus(true); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + + windowSecond->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -782,10 +810,11 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first) - windowTop->setFocus(); - windowSecond->setFocus(); + windowTop->setFocus(true); + windowSecond->setFocus(true); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + windowTop->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -805,11 +834,12 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - windowTop->setFocus(); - windowSecond->setFocus(); + windowTop->setFocus(true); + windowSecond->setFocus(true); // Release channel for window is no longer valid. windowTop->releaseChannel(); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + windowSecond->consumeFocusEvent(true); // Test inject a key down, should dispatch to a valid window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -849,9 +879,10 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFocus(); + window->setFocus(true); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyKey(&keyArgs); @@ -890,6 +921,59 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + window->consumeFocusEvent(true); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); +} + +TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + mDispatcher->waitForIdle(); + + window->assertNoEvents(); +} + +// If a window is touchable, but does not have focus, it should receive motion events, but not keys +TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + // Send key + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + // Send motion + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + + // Window should receive only the motion event + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); // Key event or focus event will not be received +} + class FakeMonitorReceiver { public: FakeMonitorReceiver(const sp& dispatcher, const std::string name, @@ -946,9 +1030,10 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocus(); + window->setFocus(true); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1010,6 +1095,49 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } +/** + * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to + * the device default right away. In the test scenario, we check both the default value, + * and the action of enabling / disabling. + */ +TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocus(true); + + SCOPED_TRACE("Check default value of touch mode"); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + SCOPED_TRACE("Remove the window to trigger focus loss"); + window->setFocus(false); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); + + SCOPED_TRACE("Disable touch mode"); + mDispatcher->setInTouchMode(false); + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); + + SCOPED_TRACE("Remove the window to trigger focus loss"); + window->setFocus(false); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); + + SCOPED_TRACE("Enable touch mode again"); + mDispatcher->setInTouchMode(true); + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + window->assertNoEvents(); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: @@ -1023,8 +1151,9 @@ public: // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); - windowInPrimary->setFocus(); + windowInPrimary->setFocus(true); mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); + windowInPrimary->consumeFocusEvent(true); application2 = new FakeApplicationHandle(); windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2", @@ -1034,8 +1163,9 @@ public: mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); - windowInSecondary->setFocus(); + windowInSecondary->setFocus(true); mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); + windowInSecondary->consumeFocusEvent(true); } virtual void TearDown() override { @@ -1094,6 +1224,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; windowInPrimary->assertNoEvents(); + windowInSecondary->consumeFocusEvent(false); windowInSecondary->assertNoEvents(); } @@ -1244,10 +1375,11 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mFocusedWindow->setFocus(); + mFocusedWindow->setFocus(true); // Expect one focus window exist in display. mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); + mFocusedWindow->consumeFocusEvent(true); } virtual void TearDown() override { @@ -1350,7 +1482,7 @@ protected: void consumeMotionEvent(const sp& window, int32_t expectedAction, const std::vector& points) { - std::string name = window->mName; + const std::string name = window->getName(); InputEvent* event = window->consume(); ASSERT_NE(nullptr, event) << name.c_str() -- cgit v1.2.3-59-g8ed1b From 97b8eec72bbb08f966487fa60f3e1e7c34b25938 Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Thu, 9 Jan 2020 18:09:08 +0000 Subject: Handle different scale and offset for pointers in InputTarget. This change allows an InputTarget to have multiple pointers in different scale and offsets. This was done by using an array to store each pointerId to PointerInfo. PointerInfo contains the information that's unique to each pointer, scale and offset. Once the dispatcher is ready to queue the event, the dispatcher will request the InputTarget to gather all it's pointer information and normalize them to a single offset and scale. It will generate a new DispatcherEntry with a new MotionEvent that has all the coordinates in a single offset and scale. It will also set that offset and scale in the MotionEvent so it can be handled properly when sent to the client. Change-Id: I1793725760142e01dc3fe2372f76ecd44ad934b8 Test: InputDispatcherMultiWindowSameTokenTests Test: manually with magnification Test: NexusLauncherOutOfProcTests:TaplTestsLauncher3 Test: NexusLauncherOutOfProcTests:TaplTestsQuickstep Fixes: 140756730 Fixes: 147371357 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 144 +++++++++++++++------ services/inputflinger/dispatcher/InputTarget.cpp | 59 +++++++++ services/inputflinger/dispatcher/InputTarget.h | 47 ++++++- .../inputflinger/tests/InputDispatcher_test.cpp | 103 +++++++++++++++ 4 files changed, 305 insertions(+), 48 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a3cb4f8f68..6157d997fd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } +static std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, + EventEntry* eventEntry, + int32_t inputTargetFlags) { + if (inputTarget.useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); + return std::make_unique(eventEntry, // increments ref + inputTargetFlags, pointerInfo.xOffset, + pointerInfo.yOffset, inputTarget.globalScaleFactor, + pointerInfo.windowXScale, pointerInfo.windowYScale); + } + + ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); + const MotionEntry& motionEntry = static_cast(*eventEntry); + + PointerCoords pointerCoords[MAX_POINTERS]; + + // Use the first pointer information to normalize all other pointers. This could be any pointer + // as long as all other pointers are normalized to the same value and the final DispatchEntry + // uses the offset and scale for the normalized pointer. + const PointerInfo& firstPointerInfo = + inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; + + // Iterate through all pointers in the event to normalize against the first. + for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { + const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; + + // The scale factor is the ratio of the current pointers scale to the normalized scale. + float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; + float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; + + pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); + // First apply the current pointers offset to set the window at 0,0 + pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); + // Next scale the coordinates. + pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); + // Lastly, offset the coordinates so they're in the normalized pointer's frame. + pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, + -firstPointerInfo.yOffset); + } + + MotionEntry* combinedMotionEntry = + new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, + motionEntry.action, motionEntry.actionButton, motionEntry.flags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.pointerCount, + motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, + 0 /* yOffset */); + + return std::make_unique(combinedMotionEntry, // increments ref + inputTargetFlags, firstPointerInfo.xOffset, + firstPointerInfo.yOffset, inputTarget.globalScaleFactor, + firstPointerInfo.windowXScale, + firstPointerInfo.windowYScale); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1785,23 +1846,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } + std::vector::iterator it = + std::find_if(inputTargets.begin(), inputTargets.end(), + [&windowHandle](const InputTarget& inputTarget) { + return inputTarget.inputChannel->getConnectionToken() == + windowHandle->getToken(); + }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget target; - target.inputChannel = inputChannel; - target.flags = targetFlags; - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - target.pointerIds = pointerIds; - inputTargets.push_back(target); + + if (it == inputTargets.end()) { + InputTarget inputTarget; + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + inputTarget.inputChannel = inputChannel; + inputTarget.flags = targetFlags; + inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTargets.push_back(inputTarget); + it = inputTargets.end() - 1; + } + + ALOG_ASSERT(it->flags == targetFlags); + ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); + + it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, @@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = xOffset; - target.yOffset = yOffset; - target.pointerIds.clear(); - target.globalScaleFactor = 1.0f; + target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } @@ -2054,11 +2123,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " - "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, - inputTarget.windowYScale, inputTarget.pointerIds.value); + "globalScaleFactor=%f, pointerIds=0x%x %s", + connection->getInputChannelName().c_str(), inputTarget.flags, + inputTarget.globalScaleFactor, inputTarget.pointerIds.value, + inputTarget.getPointerInfoString().c_str()); #endif // Skip this event if the connection status is not normal. @@ -2152,15 +2220,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr dispatchEntry = - std::make_unique(eventEntry, // increments ref - inputTargetFlags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, - inputTarget.windowXScale, inputTarget.windowYScale); + createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); + // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a + // different EventEntry than what was passed in. + EventEntry* newEntry = dispatchEntry->eventEntry; // Apply target flags and update the connection's input state. - switch (eventEntry->type) { + switch (newEntry->type) { case EventEntry::Type::KEY: { - const KeyEntry& keyEntry = static_cast(*eventEntry); + const KeyEntry& keyEntry = static_cast(*newEntry); dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2176,7 +2244,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast(*eventEntry); + const MotionEntry& motionEntry = static_cast(*newEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2227,14 +2295,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(eventEntry->type)); + EventEntry::typeToString(newEntry->type)); break; } } // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatches(eventEntry); + incrementPendingForegroundDispatches(newEntry); } // Enqueue the dispatch entry. @@ -2611,15 +2679,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - } else { - target.xOffset = 0; - target.yOffset = 0; - target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 80fa2cb995..0588374292 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,4 +42,63 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } +void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, + float windowXScale, float windowYScale) { + // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors + // and non splittable windows since we will just use all the pointers from the input event. + if (newPointerIds.isEmpty()) { + setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); + return; + } + + // Ensure that the new set of pointers doesn't overlap with the current set of pointers. + ALOG_ASSERT((pointerIds & newPointerIds) == 0); + + pointerIds |= newPointerIds; + while (!newPointerIds.isEmpty()) { + int32_t pointerId = newPointerIds.clearFirstMarkedBit(); + pointerInfos[pointerId].xOffset = xOffset; + pointerInfos[pointerId].yOffset = yOffset; + pointerInfos[pointerId].windowXScale = windowXScale; + pointerInfos[pointerId].windowYScale = windowYScale; + } +} + +void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale) { + pointerIds.clear(); + pointerInfos[0].xOffset = xOffset; + pointerInfos[0].yOffset = yOffset; + pointerInfos[0].windowXScale = windowXScale; + pointerInfos[0].windowYScale = windowYScale; +} + +bool InputTarget::useDefaultPointerInfo() const { + return pointerIds.isEmpty(); +} + +const PointerInfo& InputTarget::getDefaultPointerInfo() const { + return pointerInfos[0]; +} + +std::string InputTarget::getPointerInfoString() const { + if (useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = getDefaultPointerInfo(); + return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)", + pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale, + pointerInfo.windowYScale); + } + + std::string out; + for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) { + if (!pointerIds.hasBit(i)) { + continue; + } + out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f " + "windowScaleFactor=(%.1f, %.1f)", + i, pointerInfos[i].xOffset, pointerInfos[i].yOffset, + pointerInfos[i].windowXScale, pointerInfos[i].windowYScale); + } + return out; +} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 1ba5effada..499a75fdac 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -23,6 +23,22 @@ namespace android::inputdispatcher { +/* + * Information about each pointer for an InputTarget. This includes offset and scale so + * all pointers can be normalized to a single offset and scale. + * + * These values are ignored for KeyEvents + */ +struct PointerInfo { + // The x and y offset to add to a MotionEvent as it is delivered. + float xOffset = 0.0f; + float yOffset = 0.0f; + + // Scaling factor to apply to MotionEvent as it is delivered. + float windowXScale = 1.0f; + float windowYScale = 1.0f; +}; + /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to @@ -95,20 +111,37 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset = 0.0f; - float yOffset = 0.0f; - // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - float windowXScale = 1.0f; - float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; + // The data is stored by the pointerId. Use the bit position of pointerIds to look up + // PointerInfo per pointerId. + PointerInfo pointerInfos[MAX_POINTERS]; + + void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, + float windowYScale); + void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale); + + /** + * Returns whether the default pointer information should be used. This will be true when the + * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors + * and non splittable windows since we want all pointers for the EventEntry to go to this + * target. + */ + bool useDefaultPointerInfo() const; + + /** + * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is + * true. + */ + const PointerInfo& getDefaultPointerInfo() const; + + std::string getPointerInfoString() const; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5ffc89d210..bd8d2a4d39 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { + mWindow1->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 114b77f9b728e812d3d96a9ab8c1a7a32e05a49d Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Wed, 15 Jan 2020 22:35:10 +0000 Subject: Revert "Handle different scale and offset for pointers in InputTarget." This reverts commit 97b8eec72bbb08f966487fa60f3e1e7c34b25938. Reason for revert: b/147666669 Change-Id: Ib7cb046be30224416f813ca98c54899ff651f7be --- .../inputflinger/dispatcher/InputDispatcher.cpp | 144 ++++++--------------- services/inputflinger/dispatcher/InputTarget.cpp | 59 --------- services/inputflinger/dispatcher/InputTarget.h | 47 +------ .../inputflinger/tests/InputDispatcher_test.cpp | 103 --------------- 4 files changed, 48 insertions(+), 305 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 6157d997fd..a3cb4f8f68 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,67 +256,6 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } -static std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, - EventEntry* eventEntry, - int32_t inputTargetFlags) { - if (inputTarget.useDefaultPointerInfo()) { - const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); - return std::make_unique(eventEntry, // increments ref - inputTargetFlags, pointerInfo.xOffset, - pointerInfo.yOffset, inputTarget.globalScaleFactor, - pointerInfo.windowXScale, pointerInfo.windowYScale); - } - - ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); - const MotionEntry& motionEntry = static_cast(*eventEntry); - - PointerCoords pointerCoords[MAX_POINTERS]; - - // Use the first pointer information to normalize all other pointers. This could be any pointer - // as long as all other pointers are normalized to the same value and the final DispatchEntry - // uses the offset and scale for the normalized pointer. - const PointerInfo& firstPointerInfo = - inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; - - // Iterate through all pointers in the event to normalize against the first. - for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { - const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; - - // The scale factor is the ratio of the current pointers scale to the normalized scale. - float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; - float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; - - pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); - // First apply the current pointers offset to set the window at 0,0 - pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); - // Next scale the coordinates. - pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); - // Lastly, offset the coordinates so they're in the normalized pointer's frame. - pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, - -firstPointerInfo.yOffset); - } - - MotionEntry* combinedMotionEntry = - new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, - motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, - motionEntry.action, motionEntry.actionButton, motionEntry.flags, - motionEntry.metaState, motionEntry.buttonState, - motionEntry.classification, motionEntry.edgeFlags, - motionEntry.xPrecision, motionEntry.yPrecision, - motionEntry.xCursorPosition, motionEntry.yCursorPosition, - motionEntry.downTime, motionEntry.pointerCount, - motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, - 0 /* yOffset */); - - return std::make_unique(combinedMotionEntry, // increments ref - inputTargetFlags, firstPointerInfo.xOffset, - firstPointerInfo.yOffset, inputTarget.globalScaleFactor, - firstPointerInfo.windowXScale, - firstPointerInfo.windowYScale); -} - // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1846,34 +1785,23 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - std::vector::iterator it = - std::find_if(inputTargets.begin(), inputTargets.end(), - [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); - }); - - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - - if (it == inputTargets.end()) { - InputTarget inputTarget; - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } - inputTarget.inputChannel = inputChannel; - inputTarget.flags = targetFlags; - inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; - inputTargets.push_back(inputTarget); - it = inputTargets.end() - 1; + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; } - ALOG_ASSERT(it->flags == targetFlags); - ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); - - it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + InputTarget target; + target.inputChannel = inputChannel; + target.flags = targetFlags; + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; + target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + target.pointerIds = pointerIds; + inputTargets.push_back(target); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, @@ -1896,7 +1824,10 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); + target.xOffset = xOffset; + target.yOffset = yOffset; + target.pointerIds.clear(); + target.globalScaleFactor = 1.0f; inputTargets.push_back(target); } @@ -2123,10 +2054,11 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "globalScaleFactor=%f, pointerIds=0x%x %s", - connection->getInputChannelName().c_str(), inputTarget.flags, - inputTarget.globalScaleFactor, inputTarget.pointerIds.value, - inputTarget.getPointerInfoString().c_str()); + "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " + "windowScaleFactor=(%f, %f), pointerIds=0x%x", + connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, + inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, + inputTarget.windowYScale, inputTarget.pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -2220,15 +2152,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr dispatchEntry = - createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); + std::make_unique(eventEntry, // increments ref + inputTargetFlags, inputTarget.xOffset, + inputTarget.yOffset, inputTarget.globalScaleFactor, + inputTarget.windowXScale, inputTarget.windowYScale); - // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a - // different EventEntry than what was passed in. - EventEntry* newEntry = dispatchEntry->eventEntry; // Apply target flags and update the connection's input state. - switch (newEntry->type) { + switch (eventEntry->type) { case EventEntry::Type::KEY: { - const KeyEntry& keyEntry = static_cast(*newEntry); + const KeyEntry& keyEntry = static_cast(*eventEntry); dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2244,7 +2176,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast(*newEntry); + const MotionEntry& motionEntry = static_cast(*eventEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2295,14 +2227,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(newEntry->type)); + EventEntry::typeToString(eventEntry->type)); break; } } // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatches(newEntry); + incrementPendingForegroundDispatches(eventEntry); } // Enqueue the dispatch entry. @@ -2679,9 +2611,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; target.globalScaleFactor = windowInfo->globalScaleFactor; + target.windowXScale = windowInfo->windowXScale; + target.windowYScale = windowInfo->windowYScale; + } else { + target.xOffset = 0; + target.yOffset = 0; + target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 0588374292..80fa2cb995 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,63 +42,4 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } -void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, - float windowXScale, float windowYScale) { - // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors - // and non splittable windows since we will just use all the pointers from the input event. - if (newPointerIds.isEmpty()) { - setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); - return; - } - - // Ensure that the new set of pointers doesn't overlap with the current set of pointers. - ALOG_ASSERT((pointerIds & newPointerIds) == 0); - - pointerIds |= newPointerIds; - while (!newPointerIds.isEmpty()) { - int32_t pointerId = newPointerIds.clearFirstMarkedBit(); - pointerInfos[pointerId].xOffset = xOffset; - pointerInfos[pointerId].yOffset = yOffset; - pointerInfos[pointerId].windowXScale = windowXScale; - pointerInfos[pointerId].windowYScale = windowYScale; - } -} - -void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale) { - pointerIds.clear(); - pointerInfos[0].xOffset = xOffset; - pointerInfos[0].yOffset = yOffset; - pointerInfos[0].windowXScale = windowXScale; - pointerInfos[0].windowYScale = windowYScale; -} - -bool InputTarget::useDefaultPointerInfo() const { - return pointerIds.isEmpty(); -} - -const PointerInfo& InputTarget::getDefaultPointerInfo() const { - return pointerInfos[0]; -} - -std::string InputTarget::getPointerInfoString() const { - if (useDefaultPointerInfo()) { - const PointerInfo& pointerInfo = getDefaultPointerInfo(); - return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)", - pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale, - pointerInfo.windowYScale); - } - - std::string out; - for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) { - if (!pointerIds.hasBit(i)) { - continue; - } - out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f " - "windowScaleFactor=(%.1f, %.1f)", - i, pointerInfos[i].xOffset, pointerInfos[i].yOffset, - pointerInfos[i].windowXScale, pointerInfos[i].windowYScale); - } - return out; -} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 499a75fdac..1ba5effada 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -23,22 +23,6 @@ namespace android::inputdispatcher { -/* - * Information about each pointer for an InputTarget. This includes offset and scale so - * all pointers can be normalized to a single offset and scale. - * - * These values are ignored for KeyEvents - */ -struct PointerInfo { - // The x and y offset to add to a MotionEvent as it is delivered. - float xOffset = 0.0f; - float yOffset = 0.0f; - - // Scaling factor to apply to MotionEvent as it is delivered. - float windowXScale = 1.0f; - float windowYScale = 1.0f; -}; - /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to @@ -111,37 +95,20 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset = 0.0f; + float yOffset = 0.0f; + // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; + float windowXScale = 1.0f; + float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; - // The data is stored by the pointerId. Use the bit position of pointerIds to look up - // PointerInfo per pointerId. - PointerInfo pointerInfos[MAX_POINTERS]; - - void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, - float windowYScale); - void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale); - - /** - * Returns whether the default pointer information should be used. This will be true when the - * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors - * and non splittable windows since we want all pointers for the EventEntry to go to this - * target. - */ - bool useDefaultPointerInfo() const; - - /** - * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is - * true. - */ - const PointerInfo& getDefaultPointerInfo() const; - - std::string getPointerInfoString() const; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index bd8d2a4d39..5ffc89d210 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1439,107 +1439,4 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { - mWindow2->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); -} - -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { - mWindow2->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); - - // Move both windows - touchedPoints = {{20, 20}, {175, 175}}; - expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), - getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); -} - -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { - mWindow1->setWindowScale(0.5f, 0.5f); - - // Touch Window 1 - std::vector touchedPoints = {PointF{10, 10}}; - std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); - - // Touch Window 2 - int32_t actionPointerDown = - AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); - - // Move both windows - touchedPoints = {{20, 20}, {175, 175}}; - expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), - getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); -} - } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From bd11889b7b505374cc336525dd29ac2958e47551 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 10 Jan 2020 14:08:28 -0800 Subject: Reduce indentation of a method To make it easier to figure out what happens during cancellation events, reduce the indentation of a method by returning early. Bug: none Test: none Change-Id: I2d6e3c77d70e4a7305d792d7b514d51191e338e7 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 90 +++++++++++----------- 1 file changed, 45 insertions(+), 45 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 77bf1e9d63..39b9595571 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2691,59 +2691,59 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( std::vector cancelationEvents = connection->inputState.synthesizeCancelationEvents(currentTime, options); - if (!cancelationEvents.empty()) { + if (cancelationEvents.empty()) { + return; + } #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " - "with reality: %s, mode=%d.", - connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, - options.mode); + ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " + "with reality: %s, mode=%d.", + connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, + options.mode); #endif - for (size_t i = 0; i < cancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = cancelationEvents[i]; - switch (cancelationEventEntry->type) { - case EventEntry::Type::KEY: { - logOutboundKeyDetails("cancel - ", - static_cast(*cancelationEventEntry)); - break; - } - case EventEntry::Type::MOTION: { - logOutboundMotionDetails("cancel - ", - static_cast( - *cancelationEventEntry)); - break; - } - case EventEntry::Type::FOCUS: { - LOG_ALWAYS_FATAL("Canceling focus events is not supported"); - break; - } - case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { - LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - EventEntry::typeToString(cancelationEventEntry->type)); - break; - } + for (size_t i = 0; i < cancelationEvents.size(); i++) { + EventEntry* cancelationEventEntry = cancelationEvents[i]; + switch (cancelationEventEntry->type) { + case EventEntry::Type::KEY: { + logOutboundKeyDetails("cancel - ", + static_cast(*cancelationEventEntry)); + break; } - - InputTarget target; - sp windowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); - if (windowHandle != nullptr) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); - target.globalScaleFactor = windowInfo->globalScaleFactor; + case EventEntry::Type::MOTION: { + logOutboundMotionDetails("cancel - ", + static_cast(*cancelationEventEntry)); + break; } - target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - - enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - target, InputTarget::FLAG_DISPATCH_AS_IS); + case EventEntry::Type::FOCUS: { + LOG_ALWAYS_FATAL("Canceling focus events is not supported"); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", + EventEntry::typeToString(cancelationEventEntry->type)); + break; + } + } - cancelationEventEntry->release(); + InputTarget target; + sp windowHandle = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + if (windowHandle != nullptr) { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); + target.globalScaleFactor = windowInfo->globalScaleFactor; } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - startDispatchCycleLocked(currentTime, connection); + enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref + target, InputTarget::FLAG_DISPATCH_AS_IS); + + cancelationEventEntry->release(); } + + startDispatchCycleLocked(currentTime, connection); } MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry, -- cgit v1.2.3-59-g8ed1b From 421eb1c2edc0f69d80cb9182a6d5a970501f8699 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 16 Jan 2020 00:09:42 +0800 Subject: Touch cancellation handling Cancel motion event that are accidental touches when a user is holding the device and a touch stream is in progress. - Process MT_TOOL_PALM and cancel touch when sent from firmware. Bug: 117933934 Test: atest inputflinger_tests Test: atest InputProcessTest Change-Id: I80ba8b462572e5214dd1073ef0568886f71e21c9 --- include/android/input.h | 2 + .../reader/mapper/MultiTouchInputMapper.cpp | 10 +++ services/inputflinger/tests/InputReader_test.cpp | 91 ++++++++++++++++++++++ 3 files changed, 103 insertions(+) (limited to 'services/inputflinger') diff --git a/include/android/input.h b/include/android/input.h index f51cd79504..dbfd61eb05 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -793,6 +793,8 @@ enum { AMOTION_EVENT_TOOL_TYPE_MOUSE = 3, /** eraser */ AMOTION_EVENT_TOOL_TYPE_ERASER = 4, + /** palm */ + AMOTION_EVENT_TOOL_TYPE_PALM = 5, }; /** diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index c567c8bf80..f42ddcf461 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -209,6 +209,8 @@ int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { return AMOTION_EVENT_TOOL_TYPE_FINGER; case MT_TOOL_PEN: return AMOTION_EVENT_TOOL_TYPE_STYLUS; + case MT_TOOL_PALM: + return AMOTION_EVENT_TOOL_TYPE_PALM; } } return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; @@ -247,6 +249,14 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { continue; } + if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) { + if (!mCurrentMotionAborted) { + ALOGI("Canceling touch gesture from device %s because the palm event was detected", + getDeviceName().c_str()); + cancelTouch(when); + } + } + if (outCount >= MAX_POINTERS) { #if DEBUG_POINTERS ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 1fc8217df8..a9d7793a17 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -6798,4 +6798,95 @@ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + addMapperAndConfigure(mapper); + + NotifyMotionArgs motionArgs; + + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; + // finger down + processId(mapper, 1); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // finger move + processId(mapper, 1); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // finger up. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // new finger down + processId(mapper, 1); + processPosition(mapper, x3, y3); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); +} + +/** + * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and + * UP events should be ignored. + */ +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + addMapperAndConfigure(mapper); + + NotifyMotionArgs motionArgs; + + // default tool type is finger + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; + processId(mapper, 1); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // Tool changed to MT_TOOL_PALM expect sending the cancel event. + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + + // Ignore the following MOVE and UP events if had detect a palm event. + processId(mapper, 1); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // finger up. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // new finger down + processToolType(mapper, MT_TOOL_FINGER); + processId(mapper, 1); + processPosition(mapper, x3, y3); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 65f98b83a6720a07bd3be3003363365efd89e966 Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Thu, 16 Jan 2020 18:56:50 +0000 Subject: Handle different scale and offset for pointers in InputTarget. This change allows an InputTarget to have multiple pointers in different scale and offsets. This was done by using an array to store each pointerId to PointerInfo. PointerInfo contains the information that's unique to each pointer, scale and offset. Once the dispatcher is ready to queue the event, the dispatcher will request the InputTarget to gather all it's pointer information and normalize them to a single offset and scale. It will generate a new DispatcherEntry with a new MotionEvent that has all the coordinates in a single offset and scale. It will also set that offset and scale in the MotionEvent so it can be handled properly when sent to the client. This is the 3rd attempt since it was breaking tests Change-Id: Ic75c7df06cf4a881da6ac0c57013e29a84533d1e Test: InputDispatcherMultiWindowSameTokenTests Test: manually with magnification Test: NexusLauncherOutOfProcTests:TaplTestsLauncher3 Test: NexusLauncherOutOfProcTests:TaplTestsQuickstep Fixes: 140756730 Fixes: 147371357 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 152 +++++++++++++++------ services/inputflinger/dispatcher/InputTarget.cpp | 59 ++++++++ services/inputflinger/dispatcher/InputTarget.h | 47 ++++++- .../inputflinger/tests/InputDispatcher_test.cpp | 103 ++++++++++++++ 4 files changed, 313 insertions(+), 48 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index ce7399ef6e..30fdf906fc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,6 +256,75 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } +static std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, + EventEntry* eventEntry, + int32_t inputTargetFlags) { + if (inputTarget.useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); + return std::make_unique(eventEntry, // increments ref + inputTargetFlags, pointerInfo.xOffset, + pointerInfo.yOffset, inputTarget.globalScaleFactor, + pointerInfo.windowXScale, pointerInfo.windowYScale); + } + + ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); + const MotionEntry& motionEntry = static_cast(*eventEntry); + + PointerCoords pointerCoords[motionEntry.pointerCount]; + + // Use the first pointer information to normalize all other pointers. This could be any pointer + // as long as all other pointers are normalized to the same value and the final DispatchEntry + // uses the offset and scale for the normalized pointer. + const PointerInfo& firstPointerInfo = + inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; + + // Iterate through all pointers in the event to normalize against the first. + for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { + const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; + + // The scale factor is the ratio of the current pointers scale to the normalized scale. + float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; + float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; + + pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); + // First apply the current pointers offset to set the window at 0,0 + pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); + // Next scale the coordinates. + pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); + // Lastly, offset the coordinates so they're in the normalized pointer's frame. + pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, + -firstPointerInfo.yOffset); + } + + MotionEntry* combinedMotionEntry = + new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, + motionEntry.action, motionEntry.actionButton, motionEntry.flags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.pointerCount, + motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, + 0 /* yOffset */); + + if (motionEntry.injectionState) { + combinedMotionEntry->injectionState = motionEntry.injectionState; + combinedMotionEntry->injectionState->refCount += 1; + } + + std::unique_ptr dispatchEntry = + std::make_unique(combinedMotionEntry, // increments ref + inputTargetFlags, firstPointerInfo.xOffset, + firstPointerInfo.yOffset, inputTarget.globalScaleFactor, + firstPointerInfo.windowXScale, + firstPointerInfo.windowYScale); + combinedMotionEntry->release(); + return dispatchEntry; +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1815,23 +1884,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) { - sp inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } + std::vector::iterator it = + std::find_if(inputTargets.begin(), inputTargets.end(), + [&windowHandle](const InputTarget& inputTarget) { + return inputTarget.inputChannel->getConnectionToken() == + windowHandle->getToken(); + }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget target; - target.inputChannel = inputChannel; - target.flags = targetFlags; - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - target.pointerIds = pointerIds; - inputTargets.push_back(target); + + if (it == inputTargets.end()) { + InputTarget inputTarget; + sp inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + inputTarget.inputChannel = inputChannel; + inputTarget.flags = targetFlags; + inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTargets.push_back(inputTarget); + it = inputTargets.end() - 1; + } + + ALOG_ASSERT(it->flags == targetFlags); + ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); + + it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& inputTargets, @@ -1854,10 +1934,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = xOffset; - target.yOffset = yOffset; - target.pointerIds.clear(); - target.globalScaleFactor = 1.0f; + target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } @@ -2089,11 +2166,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " - "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, - inputTarget.windowYScale, inputTarget.pointerIds.value); + "globalScaleFactor=%f, pointerIds=0x%x %s", + connection->getInputChannelName().c_str(), inputTarget.flags, + inputTarget.globalScaleFactor, inputTarget.pointerIds.value, + inputTarget.getPointerInfoString().c_str()); #endif // Skip this event if the connection status is not normal. @@ -2187,15 +2263,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr dispatchEntry = - std::make_unique(eventEntry, // increments ref - inputTargetFlags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, - inputTarget.windowXScale, inputTarget.windowYScale); + createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); + // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a + // different EventEntry than what was passed in. + EventEntry* newEntry = dispatchEntry->eventEntry; // Apply target flags and update the connection's input state. - switch (eventEntry->type) { + switch (newEntry->type) { case EventEntry::Type::KEY: { - const KeyEntry& keyEntry = static_cast(*eventEntry); + const KeyEntry& keyEntry = static_cast(*newEntry); dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2211,7 +2287,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast(*eventEntry); + const MotionEntry& motionEntry = static_cast(*newEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2265,14 +2341,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(eventEntry->type)); + EventEntry::typeToString(newEntry->type)); break; } } // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatches(eventEntry); + incrementPendingForegroundDispatches(newEntry); } // Enqueue the dispatch entry. @@ -2661,15 +2737,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - } else { - target.xOffset = 0; - target.yOffset = 0; - target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 80fa2cb995..0588374292 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,4 +42,63 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } +void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, + float windowXScale, float windowYScale) { + // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors + // and non splittable windows since we will just use all the pointers from the input event. + if (newPointerIds.isEmpty()) { + setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); + return; + } + + // Ensure that the new set of pointers doesn't overlap with the current set of pointers. + ALOG_ASSERT((pointerIds & newPointerIds) == 0); + + pointerIds |= newPointerIds; + while (!newPointerIds.isEmpty()) { + int32_t pointerId = newPointerIds.clearFirstMarkedBit(); + pointerInfos[pointerId].xOffset = xOffset; + pointerInfos[pointerId].yOffset = yOffset; + pointerInfos[pointerId].windowXScale = windowXScale; + pointerInfos[pointerId].windowYScale = windowYScale; + } +} + +void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale) { + pointerIds.clear(); + pointerInfos[0].xOffset = xOffset; + pointerInfos[0].yOffset = yOffset; + pointerInfos[0].windowXScale = windowXScale; + pointerInfos[0].windowYScale = windowYScale; +} + +bool InputTarget::useDefaultPointerInfo() const { + return pointerIds.isEmpty(); +} + +const PointerInfo& InputTarget::getDefaultPointerInfo() const { + return pointerInfos[0]; +} + +std::string InputTarget::getPointerInfoString() const { + if (useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = getDefaultPointerInfo(); + return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)", + pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale, + pointerInfo.windowYScale); + } + + std::string out; + for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) { + if (!pointerIds.hasBit(i)) { + continue; + } + out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f " + "windowScaleFactor=(%.1f, %.1f)", + i, pointerInfos[i].xOffset, pointerInfos[i].yOffset, + pointerInfos[i].windowXScale, pointerInfos[i].windowYScale); + } + return out; +} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 1ba5effada..499a75fdac 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -23,6 +23,22 @@ namespace android::inputdispatcher { +/* + * Information about each pointer for an InputTarget. This includes offset and scale so + * all pointers can be normalized to a single offset and scale. + * + * These values are ignored for KeyEvents + */ +struct PointerInfo { + // The x and y offset to add to a MotionEvent as it is delivered. + float xOffset = 0.0f; + float yOffset = 0.0f; + + // Scaling factor to apply to MotionEvent as it is delivered. + float windowXScale = 1.0f; + float windowYScale = 1.0f; +}; + /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to @@ -95,20 +111,37 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset = 0.0f; - float yOffset = 0.0f; - // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - float windowXScale = 1.0f; - float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; + // The data is stored by the pointerId. Use the bit position of pointerIds to look up + // PointerInfo per pointerId. + PointerInfo pointerInfos[MAX_POINTERS]; + + void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, + float windowYScale); + void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale); + + /** + * Returns whether the default pointer information should be used. This will be true when the + * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors + * and non splittable windows since we want all pointers for the EventEntry to go to this + * target. + */ + bool useDefaultPointerInfo() const; + + /** + * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is + * true. + */ + const PointerInfo& getDefaultPointerInfo() const; + + std::string getPointerInfoString() const; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c262abb229..98ebf50819 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1571,4 +1571,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { + mWindow1->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector touchedPoints = {PointF{10, 10}}; + std::vector expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 888a6a41edc5e4250076b7e36e4d626eb70edc71 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 9 Jan 2020 11:39:16 -0800 Subject: Let InputReader set mouse cursor's display. InputReader was already responsible to bind device/event and display. It makes sense to further extend it to control the display where mouse cursor is shown. Bug: 146385350 Test: Mouse cursor shows up at expected display. Test: atest inputflinger_tests Change-Id: Ie7a9546550b70c8834462b06de929472196fe713 --- services/inputflinger/InputReaderBase.cpp | 12 ++++++++- services/inputflinger/include/InputReaderBase.h | 6 ++++- .../include/PointerControllerInterface.h | 4 +++ .../reader/mapper/CursorInputMapper.cpp | 22 +++++++++++++++- .../inputflinger/reader/mapper/CursorInputMapper.h | 3 ++- .../reader/mapper/TouchInputMapper.cpp | 17 ++++++++++-- services/inputflinger/tests/InputReader_test.cpp | 30 +++++++++++++++------- 7 files changed, 79 insertions(+), 15 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 2d6f2c1cc9..b2dadf8460 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -125,6 +125,16 @@ std::optional InputReaderConfiguration::getDisplayViewportByPor return std::nullopt; } +std::optional InputReaderConfiguration::getDisplayViewportById( + int32_t displayId) const { + for (const DisplayViewport& currentViewport : mDisplays) { + if (currentViewport.displayId == displayId) { + return std::make_optional(currentViewport); + } + } + return std::nullopt; +} + void InputReaderConfiguration::setDisplayViewports(const std::vector& viewports) { mDisplays = viewports; } @@ -151,4 +161,4 @@ void TouchAffineTransformation::applyTo(float& x, float& y) const { y = newY; } -} // namespace android \ No newline at end of file +} // namespace android diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 56c0a7356d..f8d535152e 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -169,6 +169,9 @@ struct InputReaderConfiguration { // Used to determine which DisplayViewport should be tied to which InputDevice. std::unordered_map portAssociations; + // The suggested display ID to show the cursor. + int32_t defaultPointerDisplayId; + // Velocity control parameters for mouse pointer movements. VelocityControlParameters pointerVelocityControlParameters; @@ -273,6 +276,7 @@ struct InputReaderConfiguration { std::optional getDisplayViewportByUniqueId(const std::string& uniqueDisplayId) const; std::optional getDisplayViewportByPort(uint8_t physicalPort) const; + std::optional getDisplayViewportById(int32_t displayId) const; void setDisplayViewports(const std::vector& viewports); @@ -352,4 +356,4 @@ public: } // namespace android -#endif // _UI_INPUT_READER_COMMON_H \ No newline at end of file +#endif // _UI_INPUT_READER_COMMON_H diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 0ff28e4174..194c66547f 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -17,6 +17,7 @@ #ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H #define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H +#include #include #include #include @@ -101,6 +102,9 @@ public: /* Gets the id of the display where the pointer should be shown. */ virtual int32_t getDisplayId() const = 0; + + /* Sets the associated display of this pointer. Pointer should show on that display. */ + virtual void setDisplayViewport(const DisplayViewport& displayViewport) = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index f69138ea09..69a75bae2f 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -189,12 +189,32 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* // Update the PointerController if viewports changed. if (mParameters.mode == Parameters::MODE_POINTER) { - getPolicy()->obtainPointerController(getDeviceId()); + updatePointerControllerDisplayViewport(*config); } bumpGeneration(); } } +void CursorInputMapper::updatePointerControllerDisplayViewport( + const InputReaderConfiguration& config) { + std::optional viewport = + config.getDisplayViewportById(config.defaultPointerDisplayId); + if (!viewport) { + ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input " + "mapper. Fall back to default display", + config.defaultPointerDisplayId); + viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT); + } + + if (!viewport) { + ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to" + " PointerController."); + return; + } + + mPointerController->setDisplayViewport(*viewport); +} + void CursorInputMapper::configureParameters() { mParameters.mode = Parameters::MODE_POINTER; String8 cursorModeString; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 77d122af0a..d56f9be045 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -117,8 +117,9 @@ private: void dumpParameters(std::string& dump); void sync(nsecs_t when); + void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config); }; } // namespace android -#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H \ No newline at end of file +#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index c80a2dc4d9..b66caca787 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -552,9 +552,10 @@ bool TouchInputMapper::hasExternalStylus() const { * Determine which DisplayViewport to use. * 1. If display port is specified, return the matching viewport. If matching viewport not * found, then return. - * 2. If a device has associated display, get the matching viewport by either unique id or by + * 2. Always use the suggested viewport from WindowManagerService for pointers. + * 3. If a device has associated display, get the matching viewport by either unique id or by * the display type (internal or external). - * 3. Otherwise, use a non-display viewport. + * 4. Otherwise, use a non-display viewport. */ std::optional TouchInputMapper::findViewport() { if (mParameters.hasAssociatedDisplay) { @@ -564,6 +565,17 @@ std::optional TouchInputMapper::findViewport() { return mDevice->getAssociatedViewport(); } + if (mDeviceMode == DEVICE_MODE_POINTER) { + std::optional viewport = + mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); + if (viewport) { + return viewport; + } else { + ALOGW("Can't find designated display viewport with ID %" PRId32 " for pointers.", + mConfig.defaultPointerDisplayId); + } + } + // Check if uniqueDisplayId is specified in idc file. if (!mParameters.uniqueDisplayId.empty()) { return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); @@ -749,6 +761,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { if (mPointerController == nullptr || viewportChanged) { mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + mPointerController->setDisplayViewport(mViewport); } } else { mPointerController.clear(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index a9d7793a17..8ca7e4a893 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -90,10 +90,6 @@ public: mMaxY = maxY; } - void setDisplayId(int32_t displayId) { - mDisplayId = displayId; - } - virtual void setPosition(float x, float y) { mX = x; mY = y; @@ -116,6 +112,10 @@ public: return mDisplayId; } + virtual void setDisplayViewport(const DisplayViewport& viewport) { + mDisplayId = viewport.displayId; + } + const std::map>& getSpots() { return mSpotsByDisplay; } @@ -280,6 +280,10 @@ public: mConfig.showTouches = enabled; } + void setDefaultPointerDisplayId(int32_t pointerDisplayId) { + mConfig.defaultPointerDisplayId = pointerDisplayId; + } + private: DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, const std::string& uniqueId, std::optional physicalPort, @@ -3432,12 +3436,18 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { CursorInputMapper* mapper = new CursorInputMapper(mDevice); addMapperAndConfigure(mapper); - // Setup PointerController for second display. + // Setup for second display. constexpr int32_t SECOND_DISPLAY_ID = 1; + const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1"; + mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0, + SECOND_DISPLAY_UNIQUE_ID, NO_PORT, + ViewportType::VIEWPORT_EXTERNAL); + mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(100, 200); mFakePointerController->setButtonState(0); - mFakePointerController->setDisplayId(SECOND_DISPLAY_ID); NotifyMotionArgs args; process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10); @@ -6539,14 +6549,16 @@ TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) { } TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { - // Setup PointerController for second display. + // Setup for second display. sp fakePointerController = new FakePointerController(); - fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); fakePointerController->setPosition(100, 200); fakePointerController->setButtonState(0); - fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID); mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); -- cgit v1.2.3-59-g8ed1b From 5a57cff6f380f62841f671890e38a33aab129928 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 31 Oct 2019 18:40:33 -0700 Subject: Create InputThread to manage inputflinger threads InputReader and InputDispatcher both manage their own threads. To help manage input processing threads that have elevated priority/nice values, we create the InputThread class. Creating an InputThread starts the thread immediately, and loops the loop function until the InputThread object is destructed. Bug: 130819454 Test: atest inputflinger_tests Test: Touch input works on crosshatch Change-Id: I2cb56250fc62300e195c5b2474d32c2db3fa4711 --- services/inputflinger/Android.bp | 1 + services/inputflinger/InputThread.cpp | 60 ++++++++++++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 40 +++------------ services/inputflinger/dispatcher/InputDispatcher.h | 4 +- services/inputflinger/include/InputThread.h | 46 +++++++++++++++++ services/inputflinger/reader/InputReader.cpp | 40 +++------------ services/inputflinger/reader/include/InputReader.h | 4 +- 7 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 services/inputflinger/InputThread.cpp create mode 100644 services/inputflinger/include/InputThread.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index f6b5935bcf..308e93aa48 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -83,6 +83,7 @@ cc_library_shared { srcs: [ "InputListener.cpp", "InputReaderBase.cpp", + "InputThread.cpp", ], shared_libs: [ diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp new file mode 100644 index 0000000000..b87f7a1243 --- /dev/null +++ b/services/inputflinger/InputThread.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputThread.h" + +namespace android { + +namespace { + +// Implementation of Thread from libutils. +class InputThreadImpl : public Thread { +public: + explicit InputThreadImpl(std::function loop) + : Thread(/* canCallJava */ true), mThreadLoop(loop) {} + + ~InputThreadImpl() {} + +private: + std::function mThreadLoop; + + bool threadLoop() override { + mThreadLoop(); + return true; + } +}; + +} // namespace + +InputThread::InputThread(std::string name, std::function loop, std::function wake) + : mName(name), mThreadWake(wake) { + mThread = new InputThreadImpl(loop); + mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); +} + +InputThread::~InputThread() { + mThread->requestExit(); + if (mThreadWake) { + mThreadWake(); + } + mThread->requestExitAndWait(); +} + +bool InputThread::isCallingThread() { + return gettid() == mThread->getTid(); +} + +} // namespace android \ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 30fdf906fc..6a56d38298 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -325,24 +325,6 @@ static std::unique_ptr createDispatchEntry(const InputTarget& inp return dispatchEntry; } -// --- InputDispatcherThread --- - -class InputDispatcher::InputDispatcherThread : public Thread { -public: - explicit InputDispatcherThread(InputDispatcher* dispatcher) - : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {} - - ~InputDispatcherThread() {} - -private: - InputDispatcher* mDispatcher; - - virtual bool threadLoop() override { - mDispatcher->dispatchOnce(); - return true; - } -}; - // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp& policy) @@ -367,8 +349,6 @@ InputDispatcher::InputDispatcher(const sp& polic mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); - - mThread = new InputDispatcherThread(this); } InputDispatcher::~InputDispatcher() { @@ -387,25 +367,21 @@ InputDispatcher::~InputDispatcher() { } status_t InputDispatcher::start() { - if (mThread->isRunning()) { + if (mThread) { return ALREADY_EXISTS; } - return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); + mThread = std::make_unique( + "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); }); + return OK; } status_t InputDispatcher::stop() { - if (!mThread->isRunning()) { - return OK; - } - if (gettid() == mThread->getTid()) { - ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!"); + if (mThread && mThread->isCallingThread()) { + ALOGE("InputDispatcher cannot be stopped from its own thread!"); return INVALID_OPERATION; } - // Directly calling requestExitAndWait() causes the thread to not exit - // if mLooper is waiting for a long timeout. - mThread->requestExit(); - mLooper->wake(); - return mThread->requestExitAndWait(); + mThread.reset(); + return OK; } void InputDispatcher::dispatchOnce() { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index a4ba0dec6a..93de18d02f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -25,6 +25,7 @@ #include "InputDispatcherPolicyInterface.h" #include "InputState.h" #include "InputTarget.h" +#include "InputThread.h" #include "Monitor.h" #include "TouchState.h" #include "TouchedWindow.h" @@ -124,8 +125,7 @@ private: STALE, }; - class InputDispatcherThread; - sp mThread; + std::unique_ptr mThread; sp mPolicy; android::InputDispatcherConfiguration mConfig; diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h new file mode 100644 index 0000000000..407365a269 --- /dev/null +++ b/services/inputflinger/include/InputThread.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_THREAD_H +#define _UI_INPUT_THREAD_H + +#include + +namespace android { + +/* A thread that loops continuously until destructed to process input events. + * + * Creating the InputThread starts it immediately. The thread begins looping the loop + * function until the InputThread is destroyed. The wake function is used to wake anything + * that sleeps in the loop when it is time for the thread to be destroyed. + */ +class InputThread { +public: + explicit InputThread(std::string name, std::function loop, + std::function wake = nullptr); + virtual ~InputThread(); + + bool isCallingThread(); + +private: + std::string mName; + std::function mThreadWake; + sp mThread; +}; + +} // namespace android + +#endif // _UI_INPUT_THREAD_H \ No newline at end of file diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 05f0db1329..2023c6e8da 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -49,25 +49,6 @@ using android::base::StringPrintf; namespace android { -// --- InputReader::InputReaderThread --- - -/* Thread that reads raw events from the event hub and processes them, endlessly. */ -class InputReader::InputReaderThread : public Thread { -public: - explicit InputReaderThread(InputReader* reader) - : Thread(/* canCallJava */ true), mReader(reader) {} - - ~InputReaderThread() {} - -private: - InputReader* mReader; - - bool threadLoop() override { - mReader->loopOnce(); - return true; - } -}; - // --- InputReader --- InputReader::InputReader(std::shared_ptr eventHub, @@ -83,7 +64,6 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); - mThread = new InputReaderThread(this); { // acquire lock AutoMutex _l(mLock); @@ -100,25 +80,21 @@ InputReader::~InputReader() { } status_t InputReader::start() { - if (mThread->isRunning()) { + if (mThread) { return ALREADY_EXISTS; } - return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + mThread = std::make_unique( + "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); }); + return OK; } status_t InputReader::stop() { - if (!mThread->isRunning()) { - return OK; - } - if (gettid() == mThread->getTid()) { - ALOGE("InputReader can only be stopped from outside of the InputReaderThread!"); + if (mThread && mThread->isCallingThread()) { + ALOGE("InputReader cannot be stopped from its own thread!"); return INVALID_OPERATION; } - // Directly calling requestExitAndWait() causes the thread to not exit - // if mEventHub is waiting for a long timeout. - mThread->requestExit(); - mEventHub->wake(); - return mThread->requestExitAndWait(); + mThread.reset(); + return OK; } void InputReader::loopOnce() { diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 502490601e..02957cd6ee 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -21,6 +21,7 @@ #include "InputListener.h" #include "InputReaderBase.h" #include "InputReaderContext.h" +#include "InputThread.h" #include #include @@ -116,8 +117,7 @@ protected: friend class ContextImpl; private: - class InputReaderThread; - sp mThread; + std::unique_ptr mThread; Mutex mLock; -- cgit v1.2.3-59-g8ed1b From 9c858ac6573d32a98d24cc0da0722592873ae3ec Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 23 Jan 2020 14:20:11 -0600 Subject: Add hmac to InputEvent and scale to MotionEvent We need to pass some additional data to the app to be able to construct the MotionEvent. For proper support of getRawX/getRawY, add the xScale and yScale to MotionEvent. For the verification of input events, add hmac to InputEvent. Bug: 134977432 Bug: 140786233 Test: atest libinput_tests inputflinger_tests Change-Id: Ia3400ebbd9698549aad4d97a3b789ab7e10f6b65 --- include/input/Input.h | 40 ++++---- include/input/InputTransport.h | 31 +++--- libs/input/Input.cpp | 60 +++++++----- libs/input/InputTransport.cpp | 76 +++++++-------- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 69 ++++++++------ .../input/tests/InputPublisherAndConsumer_test.cpp | 64 ++++++++----- libs/input/tests/StructLayout_test.cpp | 52 +++++----- libs/input/tests/VelocityTracker_test.cpp | 10 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 3 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 26 ++--- .../inputflinger/tests/InputDispatcher_test.cpp | 106 +++++++++++---------- 12 files changed, 300 insertions(+), 243 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index f8718479f9..1cf58efcaf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -258,6 +259,11 @@ const char* motionClassificationToString(MotionClassification classification); */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); +/** + * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified. + */ +constexpr std::array INVALID_HMAC = {0}; + /* * Pointer coordinate data. */ @@ -356,14 +362,17 @@ public: inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } + inline std::array getHmac() const { return mHmac; } protected: - void initialize(int32_t deviceId, int32_t source, int32_t displayId); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; int32_t mDisplayId; + std::array mHmac; }; /* @@ -396,18 +405,10 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, + nsecs_t eventTime); void initialize(const KeyEvent& from); protected: @@ -463,6 +464,10 @@ public: inline void setActionButton(int32_t button) { mActionButton = button; } + inline float getXScale() const { return mXScale; } + + inline float getYScale() const { return mYScale; } + inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } @@ -624,9 +629,10 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, @@ -676,6 +682,8 @@ protected: int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; + float mXScale; + float mYScale; float mXOffset; float mYOffset; float mXPrecision; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ae47438ac8..06fd3bb364 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -76,6 +76,9 @@ struct InputMessage { } header; // Body *must* be 8 byte aligned. + // For keys and motions, rely on the fact that std::array takes up exactly as much space + // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions. + static_assert(sizeof(std::array) == 32); union Body { struct Key { uint32_t seq; @@ -84,6 +87,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t flags; int32_t keyCode; @@ -103,6 +107,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t actionButton; int32_t flags; @@ -112,6 +117,8 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); + float xScale; + float yScale; float xOffset; float yOffset; float xPrecision; @@ -269,19 +276,10 @@ public: * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. * @@ -292,9 +290,10 @@ public: * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - int32_t action, int32_t actionButton, int32_t flags, - int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8ccbc7f650..bff1b97769 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,16 +57,19 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } -void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac) { mDeviceId = deviceId; mSource = source; mDisplayId = displayId; + mHmac = hmac; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; + mHmac = from.mHmac; } // --- KeyEvent --- @@ -79,19 +82,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId); +void KeyEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -250,15 +245,16 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, - size_t pointerCount, const PointerProperties* pointerProperties, +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xScale, + float yScale, float xOffset, float yOffset, float xPrecision, + float yPrecision, float rawXCursorPosition, float rawYCursorPosition, + nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId); + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -266,6 +262,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId mMetaState = metaState; mButtonState = buttonState; mClassification = classification; + mXScale = xScale; + mYScale = yScale; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; @@ -281,7 +279,7 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -289,6 +287,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; + mXScale = other->mXScale; + mYScale = other->mYScale; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; @@ -490,6 +490,12 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); mDisplayId = parcel->readInt32(); + std::vector hmac; + status_t result = parcel->readByteVector(&hmac); + if (result != OK || hmac.size() != 32) { + return BAD_VALUE; + } + std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin()); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -497,6 +503,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast(parcel->readByte()); + mXScale = parcel->readFloat(); + mYScale = parcel->readFloat(); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); @@ -543,6 +551,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); parcel->writeInt32(mDisplayId); + std::vector hmac(mHmac.begin(), mHmac.end()); + parcel->writeByteVector(hmac); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); @@ -550,6 +560,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast(mClassification)); + parcel->writeFloat(mXScale); + parcel->writeFloat(mYScale); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); @@ -607,7 +619,7 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, - ADISPLAY_ID_NONE); + ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d53a557b28..d25a5cc0ef 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -147,6 +147,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.key.source = body.key.source; // int32_t displayId msg->body.key.displayId = body.key.displayId; + // std::array hmac + msg->body.key.hmac = body.key.hmac; // int32_t action msg->body.key.action = body.key.action; // int32_t flags @@ -174,6 +176,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.source = body.motion.source; // int32_t displayId msg->body.motion.displayId = body.motion.displayId; + // std::array hmac + msg->body.motion.hmac = body.motion.hmac; // int32_t action msg->body.motion.action = body.motion.action; // int32_t actionButton @@ -190,6 +194,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; + // float xScale + msg->body.motion.xScale = body.motion.xScale; + // float yScale + msg->body.motion.yScale = body.motion.yScale; // float xOffset msg->body.motion.xOffset = body.motion.xOffset; // float yOffset @@ -424,19 +432,11 @@ InputPublisher::InputPublisher(const sp& channel) : InputPublisher::~InputPublisher() { } -status_t InputPublisher::publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { +status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, + int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); @@ -461,6 +461,7 @@ status_t InputPublisher::publishKeyEvent( msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; + msg.body.key.hmac = hmac; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -473,11 +474,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, + float yOffset, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( @@ -489,13 +491,14 @@ status_t InputPublisher::publishMotionEvent( ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " + "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, " + "xOffset=%.1f, yOffset=%.1f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xOffset, yOffset, xPrecision, - yPrecision, downTime, eventTime, pointerCount); + motionClassificationToString(classification), xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, downTime, eventTime, pointerCount); } if (!seq) { @@ -515,6 +518,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; + msg.body.motion.hmac = hmac; msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; @@ -522,6 +526,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; + msg.body.motion.xScale = xScale; + msg.body.motion.yScale = yScale; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; @@ -1136,18 +1142,10 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize( - msg->body.key.deviceId, - msg->body.key.source, - msg->body.key.displayId, - msg->body.key.action, - msg->body.key.flags, - msg->body.key.keyCode, - msg->body.key.scanCode, - msg->body.key.metaState, - msg->body.key.repeatCount, - msg->body.key.downTime, - msg->body.key.eventTime); + event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, + msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, + msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, + msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { @@ -1164,15 +1162,15 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage } event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xOffset, msg->body.motion.yOffset, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, + msg->body.motion.yOffset, msg->body.motion.xPrecision, + msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, + msg->body.motion.yCursorPosition, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index e189d20e28..6f9b162986 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - 0, keyCode, 0, metaState, 0, time, time); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, + 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b90857c99c..8c6890f542 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,7 +26,12 @@ namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; -class BaseTest : public testing::Test { }; +class BaseTest : public testing::Test { +protected: + static constexpr std::array HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; +}; // --- PointerCoordsTest --- @@ -176,16 +181,17 @@ TEST_F(KeyEventTest, Properties) { KeyEvent event; // Initialize and get properties. - const nsecs_t ARBITRARY_DOWN_TIME = 1; - const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, - AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); + EXPECT_EQ(HMAC, event.getHmac()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -210,19 +216,17 @@ TEST_F(KeyEventTest, Properties) { class MotionEventTest : public BaseTest { protected: - static const nsecs_t ARBITRARY_DOWN_TIME; - static const nsecs_t ARBITRARY_EVENT_TIME; - static const float X_OFFSET; - static const float Y_OFFSET; + static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + static constexpr float X_SCALE = 2.0; + static constexpr float Y_SCALE = 3.0; + static constexpr float X_OFFSET = 1; + static constexpr float Y_OFFSET = 1.1; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; -const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; -const float MotionEventTest::X_OFFSET = 1.0f; -const float MotionEventTest::Y_OFFSET = 1.1f; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { PointerProperties pointerProperties[2]; @@ -254,12 +258,13 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, - ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); + X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, + pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -308,12 +313,15 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); + EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); + EXPECT_EQ(X_SCALE, event->getXScale()); + EXPECT_EQ(Y_SCALE, event->getYScale()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -570,12 +578,13 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, - 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, - 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, - pointerProperties, pointerCoords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -634,9 +643,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -654,9 +664,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, - 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, - 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 2fc77e97a0..1e51ea8b2f 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -75,6 +75,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; constexpr int32_t keyCode = AKEYCODE_ENTER; @@ -84,8 +87,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, + eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -105,6 +109,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); + EXPECT_EQ(hmac, keyEvent->getHmac()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -136,6 +141,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; constexpr int32_t actionButton = 0; constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; @@ -143,6 +151,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + constexpr float xScale = 2; + constexpr float yScale = 3; constexpr float xOffset = -10; constexpr float yOffset = -20; constexpr float xPrecision = 0.25; @@ -171,12 +181,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = - mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action, + actionButton, flags, edgeFlags, metaState, buttonState, + classification, xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, xCursorPosition, + yCursorPosition, downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -196,12 +206,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); + EXPECT_EQ(hmac, motionEvent->getHmac()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); + EXPECT_EQ(xScale, motionEvent->getXScale()); + EXPECT_EQ(yScale, motionEvent->getYScale()); + EXPECT_EQ(xOffset, motionEvent->getXOffset()); + EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); @@ -316,11 +331,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -331,11 +347,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -351,11 +368,12 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 9ab0dba08d..aa8a2d488f 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -39,35 +39,39 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); CHECK_OFFSET(InputMessage::Body::Key, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Key, action, 28); - CHECK_OFFSET(InputMessage::Body::Key, flags, 32); - CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36); - CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40); - CHECK_OFFSET(InputMessage::Body::Key, metaState, 44); - CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48); - CHECK_OFFSET(InputMessage::Body::Key, downTime, 56); + CHECK_OFFSET(InputMessage::Body::Key, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Key, action, 60); + CHECK_OFFSET(InputMessage::Body::Key, flags, 64); + CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68); + CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72); + CHECK_OFFSET(InputMessage::Body::Key, metaState, 76); + CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80); + CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Motion, action, 28); - CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32); - CHECK_OFFSET(InputMessage::Body::Motion, flags, 36); - CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40); - CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44); - CHECK_OFFSET(InputMessage::Body::Motion, classification, 48); - CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52); - CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56); - CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64); - CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); - CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); - CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); - CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Motion, action, 60); + CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64); + CHECK_OFFSET(InputMessage::Body::Motion, flags, 68); + CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72); + CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76); + CHECK_OFFSET(InputMessage::Body::Motion, classification, 80); + CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84); + CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96); + CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100); + CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104); + CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108); + CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112); + CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); @@ -86,7 +90,7 @@ void TestHeaderSize() { * the Motion type, where "pointerCount" variable affects the size and can change at runtime. */ void TestBodySize() { - static_assert(sizeof(InputMessage::Body::Key) == 64); + static_assert(sizeof(InputMessage::Body::Key) == 96); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 968e2fa6bc..731eb6a000 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,11 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, + action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, + 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 246e735330..9a6ef21724 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,9 +203,10 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 30fdf906fc..d8e91cc6ad 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2410,7 +2410,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, @@ -2457,12 +2457,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - motionEntry->classification, xOffset, yOffset, + motionEntry->classification, + dispatchEntry->windowXScale, + dispatchEntry->windowYScale, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, @@ -2930,8 +2932,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, - args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, + flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, + args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3024,9 +3027,10 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - args->actionButton, args->flags, args->edgeFlags, args->metaState, - args->buttonState, args->classification, 0, 0, args->xPrecision, + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, args->actionButton, args->flags, args->edgeFlags, + args->metaState, args->buttonState, args->classification, 1 /*xScale*/, + 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); @@ -3126,7 +3130,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(keyEvent.getDeviceId(), action, /*byref*/ keyCode, /*byref*/ metaState); keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), - keyEvent.getDisplayId(), action, flags, keyCode, + keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), keyEvent.getDownTime(), keyEvent.getEventTime()); @@ -4682,8 +4686,8 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags, - entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, + event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, + entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 98ebf50819..094452a0c0 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -225,18 +225,18 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - /*action*/ -1, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + /*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, 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, ADISPLAY_ID_NONE, - AKEY_EVENT_ACTION_MULTIPLE, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + 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, 0)) @@ -260,10 +260,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -271,24 +271,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -296,24 +296,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -321,20 +321,20 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -343,10 +343,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -354,10 +354,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -367,10 +367,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -645,9 +645,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, - AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -674,10 +674,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, + /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. -- cgit v1.2.3-59-g8ed1b From 8235709159abca9504968f08825840d3e1f5d61a Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 28 Jan 2020 13:13:06 -0800 Subject: Send raw coordinates to client but scale when getting relative The current code scales the coordinates before sending to the client. This changes the coordinates so when the client requests raw coordinates, they are actually getting the scaled ones. Instead, don't scale the coordinates in InputDispatcher and instead send the scale factors to the client. When the client requests raw coordinates, they will get the unscaled ones. When they request relative coordinates, they will get the coordinates with the scale and offset applied. Fixes: 140786233 Test: atest libinput_tests Change-Id: I99b9ce7236511f595a8780506bf5aea8c75ed577 --- libs/input/Input.cpp | 42 +++++++++++----------- libs/input/tests/InputEvent_test.cpp | 30 ++++++++-------- .../input/tests/InputPublisherAndConsumer_test.cpp | 12 +++---- .../inputflinger/dispatcher/InputDispatcher.cpp | 30 ++++++++-------- 4 files changed, 57 insertions(+), 57 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bff1b97769..975483bf04 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -321,17 +321,17 @@ void MotionEvent::addSample( float MotionEvent::getXCursorPosition() const { const float rawX = getRawXCursorPosition(); - return rawX + mXOffset; + return rawX * mXScale + mXOffset; } float MotionEvent::getYCursorPosition() const { const float rawY = getRawYCursorPosition(); - return rawY + mYOffset; + return rawY * mYScale + mYOffset; } void MotionEvent::setCursorPosition(float x, float y) { - mRawXCursorPosition = x - mXOffset; - mRawYCursorPosition = y - mYOffset; + mRawXCursorPosition = (x - mXOffset) / mXScale; + mRawYCursorPosition = (y - mYOffset) / mYScale; } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { @@ -346,9 +346,9 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - return value + mXOffset; + return value * mXScale + mXOffset; case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; + return value * mYScale + mYOffset; } return value; } @@ -368,9 +368,9 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - return value + mXOffset; + return value * mXScale + mXOffset; case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; + return value * mYScale + mYOffset; } return value; } @@ -442,11 +442,11 @@ void MotionEvent::transform(const float matrix[9]) { float oldXOffset = mXOffset; float oldYOffset = mYOffset; float newX, newY; - float rawX = getRawX(0); - float rawY = getRawY(0); - transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY); - mXOffset = newX - rawX; - mYOffset = newY - rawY; + float scaledRawX = getRawX(0) * mXScale; + float scaledRawY = getRawY(0) * mYScale; + transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY); + mXOffset = newX - scaledRawX; + mYOffset = newY - scaledRawY; // Determine how the origin is transformed by the matrix so that we // can transform orientation vectors. @@ -455,22 +455,22 @@ void MotionEvent::transform(const float matrix[9]) { // Apply the transformation to cursor position. if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - float x = mRawXCursorPosition + oldXOffset; - float y = mRawYCursorPosition + oldYOffset; + float x = mRawXCursorPosition * mXScale + oldXOffset; + float y = mRawYCursorPosition * mYScale + oldYOffset; transformPoint(matrix, x, y, &x, &y); - mRawXCursorPosition = x - mXOffset; - mRawYCursorPosition = y - mYOffset; + mRawXCursorPosition = (x - mXOffset) / mXScale; + mRawYCursorPosition = (y - mYOffset) / mYScale; } // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset; - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset; + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset; transformPoint(matrix, x, y, &x, &y); - c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset); - c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset); + c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale); + c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale); float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 8c6890f542..23043b50d6 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -375,19 +375,19 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(211, event->getRawY(0)); ASSERT_EQ(221, event->getRawY(1)); - ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); - ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); - ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); - ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); - ASSERT_EQ(X_OFFSET + 210, event->getX(0)); - ASSERT_EQ(X_OFFSET + 220, event->getX(1)); - - ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); - ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); - ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); - ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); - ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); - ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); + ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); + ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); + ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1)); + ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1)); + ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0)); + ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1)); + + ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0)); + ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0)); + ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1)); + ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1)); + ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0)); + ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1)); ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); @@ -513,8 +513,8 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(210 * 2, event.getRawX(0)); ASSERT_EQ(211 * 2, event.getRawY(0)); - ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); - ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); + ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); + ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); ASSERT_EQ(212, event.getPressure(0)); ASSERT_EQ(213, event.getSize(0)); ASSERT_EQ(214 * 2, event.getTouchMajor(0)); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 1e51ea8b2f..cec9cba7d7 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -221,8 +221,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); - EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition()); - EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition()); + EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); + EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -237,10 +237,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { motionEvent->getRawX(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getRawY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, - motionEvent->getX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, - motionEvent->getY(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset, + motionEvent->getY(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5ae24199d8..3635f3b00a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2424,26 +2424,28 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, PointerCoords scaledCoords[MAX_POINTERS]; const PointerCoords* usingCoords = motionEntry->pointerCoords; - // Set the X and Y offset depending on the input source. - float xOffset, yOffset; + // Set the X and Y offset and X and Y scale depending on the input source. + float xOffset = 0.0f, yOffset = 0.0f; + float xScale = 1.0f, yScale = 1.0f; if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { float globalScaleFactor = dispatchEntry->globalScaleFactor; - float wxs = dispatchEntry->windowXScale; - float wys = dispatchEntry->windowYScale; - xOffset = dispatchEntry->xOffset * wxs; - yOffset = dispatchEntry->yOffset * wys; - if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { + xScale = dispatchEntry->windowXScale; + yScale = dispatchEntry->windowYScale; + xOffset = dispatchEntry->xOffset * xScale; + yOffset = dispatchEntry->yOffset * yScale; + if (globalScaleFactor != 1.0f) { for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i] = motionEntry->pointerCoords[i]; - scaledCoords[i].scale(globalScaleFactor, wxs, wys); + // Don't apply window scale here since we don't want scale to affect raw + // coordinates. The scale will be sent back to the client and applied + // later when requesting relative coordinates. + scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */, + 1 /* windowYScale */); } usingCoords = scaledCoords; } } else { - xOffset = 0.0f; - yOffset = 0.0f; - // We don't want the dispatch target to know. if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { @@ -2462,10 +2464,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - motionEntry->classification, - dispatchEntry->windowXScale, - dispatchEntry->windowYScale, xOffset, yOffset, - motionEntry->xPrecision, + motionEntry->classification, xScale, yScale, + xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, motionEntry->yCursorPosition, -- cgit v1.2.3-59-g8ed1b From a940fc6f0c5c63c3edeee1ea1659c7fe2199cae3 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 31 Jan 2020 19:05:41 -0800 Subject: TouchInputMapper: Use default viewport for PointerController There was a possibility of accidental mouse warping between multiple displays. InputReader has the responsibility of setting the DisplayViewport that the pointer is associated with, but framework sets a recommended "defaultPointerDisplayId". TouchInputMapper uses PointerController for touchpads (DEVICE_MODE_POINTER) and for when "showTouches" (touch spots) are enabled. In the latter case, TouchInputMapper was setting the pointer's DisplayViewport to the viewport that the mapper itself was associated with. When this viewport differs from the one recommended by framework, there is a possibility that the pointer is moved unexpectedly to the display associated with the mapper. The possibility of this happening depends upon the the order in which the various input mappers are configured. This CL removes the possibility of unexpected warping by making TouchInputMapper always use the viewport recommended by framework. Bug: 146385350 Test: atest inputflinger_tests Change-Id: Id73c59bce825855bc8c829c3a5a18c141593057e --- services/inputflinger/reader/mapper/TouchInputMapper.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index b66caca787..3b20173fa4 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -761,7 +761,11 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { if (mPointerController == nullptr || viewportChanged) { mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - mPointerController->setDisplayViewport(mViewport); + // Set the DisplayViewport for the PointerController to the default pointer display + // that is recommended by the configuration before using it. + std::optional defaultViewport = + mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); + mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport)); } } else { mPointerController.clear(); -- cgit v1.2.3-59-g8ed1b From 5d3bc374a72f5ee7315a3d4143813c615bf1ccaf Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Sun, 26 Jan 2020 23:11:07 -0800 Subject: Generate down events when transferring touch focus When we transfer touch focus to another window we are now sending down events for all pointers that would be delivered tot his window to ensure it sees a consistent event stream. Bug:146535667 Test: atest CtsAutofillTestCases atest inputflinger_tests atest com.android.server.wm.DragDropControllerTests atest android.server.wm.DragDropTest atest android.server.wm.CrossAppDragAndDropTests added dedicated tests to inputflinger_tests Change-Id: Ic745df966dbd873da94bb6d8061552ce1a5b27b0 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 87 ++++++++-- services/inputflinger/dispatcher/InputDispatcher.h | 3 + services/inputflinger/dispatcher/InputState.cpp | 94 +++++++++-- services/inputflinger/dispatcher/InputState.h | 12 +- .../inputflinger/tests/InputDispatcher_test.cpp | 187 ++++++++++++++++++++- 5 files changed, 356 insertions(+), 27 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index b2b5145097..f2b95e7f7a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2686,6 +2686,19 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, options.mode); #endif + + InputTarget target; + sp windowHandle = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + if (windowHandle != nullptr) { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); + target.globalScaleFactor = windowInfo->globalScaleFactor; + } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + for (size_t i = 0; i < cancelationEvents.size(); i++) { EventEntry* cancelationEventEntry = cancelationEvents[i]; switch (cancelationEventEntry->type) { @@ -2711,18 +2724,6 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } } - InputTarget target; - sp windowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); - if (windowHandle != nullptr) { - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); - target.globalScaleFactor = windowInfo->globalScaleFactor; - } - target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref target, InputTarget::FLAG_DISPATCH_AS_IS); @@ -2732,6 +2733,65 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( startDispatchCycleLocked(currentTime, connection); } +void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( + const sp& connection) { + if (connection->status == Connection::STATUS_BROKEN) { + return; + } + + nsecs_t currentTime = now(); + + std::vector downEvents = + connection->inputState.synthesizePointerDownEvents(currentTime); + + if (downEvents.empty()) { + return; + } + +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.", + connection->getInputChannelName().c_str(), downEvents.size()); +#endif + + InputTarget target; + sp windowHandle = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + if (windowHandle != nullptr) { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); + target.globalScaleFactor = windowInfo->globalScaleFactor; + } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + + for (EventEntry* downEventEntry : downEvents) { + switch (downEventEntry->type) { + case EventEntry::Type::MOTION: { + logOutboundMotionDetails("down - ", + static_cast(*downEventEntry)); + break; + } + + case EventEntry::Type::KEY: + case EventEntry::Type::FOCUS: + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { + LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", + EventEntry::typeToString(downEventEntry->type)); + break; + } + } + + enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref + target, InputTarget::FLAG_DISPATCH_AS_IS); + + downEventEntry->release(); + } + + startDispatchCycleLocked(currentTime, connection); +} + MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); @@ -3770,11 +3830,12 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< sp fromConnection = getConnectionLocked(fromToken); sp toConnection = getConnectionLocked(toToken); if (fromConnection != nullptr && toConnection != nullptr) { - fromConnection->inputState.copyPointerStateTo(toConnection->inputState); + fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); + synthesizePointerDownEventsForConnectionLocked(toConnection); } if (DEBUG_FOCUS) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 93de18d02f..d2aea80069 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -417,6 +417,9 @@ private: const CancelationOptions& options) REQUIRES(mLock); + void synthesizePointerDownEventsForConnectionLocked(const sp& connection) + REQUIRES(mLock); + // Splitting motion events across windows. MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index c43e304758..053598ab02 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -145,10 +145,13 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t f // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. return true; } + if (index >= 0) { MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - return true; + if (memento.firstNewPointerIdx < 0) { + memento.setPointers(entry); + return true; + } } #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion pointer up/down or move event: " @@ -249,6 +252,17 @@ void InputState::MotionMemento::setPointers(const MotionEntry& entry) { } } +void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const { + for (uint32_t i = 0; i < pointerCount; i++) { + if (other.firstNewPointerIdx < 0) { + other.firstNewPointerIdx = other.pointerCount; + } + other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]); + other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]); + other.pointerCount++; + } +} + std::vector InputState::synthesizeCancelationEvents( nsecs_t currentTime, const CancelationOptions& options) { std::vector events; @@ -282,27 +296,87 @@ std::vector InputState::synthesizeCancelationEvents( return events; } +std::vector InputState::synthesizePointerDownEvents(nsecs_t currentTime) { + std::vector events; + for (MotionMemento& memento : mMotionMementos) { + if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) { + continue; + } + + if (memento.firstNewPointerIdx < 0) { + continue; + } + + uint32_t pointerCount = 0; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + + // We will deliver all pointers the target already knows about + for (uint32_t i = 0; i < static_cast(memento.firstNewPointerIdx); i++) { + pointerProperties[i].copyFrom(memento.pointerProperties[i]); + pointerCoords[i].copyFrom(memento.pointerCoords[i]); + pointerCount++; + } + + // We will send explicit events for all pointers the target doesn't know about + for (uint32_t i = static_cast(memento.firstNewPointerIdx); + i < memento.pointerCount; i++) { + + pointerProperties[i].copyFrom(memento.pointerProperties[i]); + pointerCoords[i].copyFrom(memento.pointerCoords[i]); + pointerCount++; + + // Down only if the first pointer, pointer down otherwise + const int32_t action = (pointerCount <= 1) + ? AMOTION_EVENT_ACTION_DOWN + : AMOTION_EVENT_ACTION_POINTER_DOWN + | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, action, 0 /*actionButton*/, + memento.flags, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + pointerCount, pointerProperties, + pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/)); + } + + memento.firstNewPointerIdx = INVALID_POINTER_INDEX; + } + + return events; +} + void InputState::clear() { mKeyMementos.clear(); mMotionMementos.clear(); mFallbackKeys.clear(); } -void InputState::copyPointerStateTo(InputState& other) const { +void InputState::mergePointerStateTo(InputState& other) { for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; + MotionMemento& memento = mMotionMementos[i]; + // Since we support split pointers we need to merge touch events + // from the same source + device + screen. if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { - for (size_t j = 0; j < other.mMotionMementos.size();) { - const MotionMemento& otherMemento = other.mMotionMementos[j]; + bool merged = false; + for (size_t j = 0; j < other.mMotionMementos.size(); j++) { + MotionMemento& otherMemento = other.mMotionMementos[j]; if (memento.deviceId == otherMemento.deviceId && memento.source == otherMemento.source && memento.displayId == otherMemento.displayId) { - other.mMotionMementos.erase(other.mMotionMementos.begin() + j); - } else { - j += 1; + memento.mergePointerStateTo(otherMemento); + merged = true; + break; } } - other.mMotionMementos.push_back(memento); + if (!merged) { + memento.firstNewPointerIdx = 0; + other.mMotionMementos.push_back(memento); + } } } } diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index a93f486596..08266ae9cd 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -24,6 +24,8 @@ namespace android::inputdispatcher { +static constexpr int32_t INVALID_POINTER_INDEX = -1; + /* Tracks dispatched key and motion event state so that cancellation events can be * synthesized when events are dropped. */ class InputState { @@ -52,11 +54,14 @@ public: std::vector synthesizeCancelationEvents(nsecs_t currentTime, const CancelationOptions& options); + // Synthesizes down events for the current state. + std::vector synthesizePointerDownEvents(nsecs_t currentTime); + // Clears the current state. void clear(); - // Copies pointer-related parts of the input state to another instance. - void copyPointerStateTo(InputState& other) const; + // Merges pointer-related parts of the input state into another instance. + void mergePointerStateTo(InputState& other); // Gets the fallback key associated with a keycode. // Returns -1 if none. @@ -97,10 +102,13 @@ private: uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; + // Track for which pointers the target doesn't know about. + int32_t firstNewPointerIdx = INVALID_POINTER_INDEX; bool hovering; uint32_t policyFlags; void setPointers(const MotionEntry& entry); + void mergePointerStateTo(MotionMemento& other) const; }; std::vector mKeyMementos; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 094452a0c0..27db8f5534 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -594,12 +594,40 @@ public: expectedFlags); } - void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId, + expectedFlags); + } + + void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId, + expectedFlags); + } + + void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); } - void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + void consumeMotionPointerDown(int32_t pointerIdx, + int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { + int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN + | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags); + } + + void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + int32_t action = AMOTION_EVENT_ACTION_POINTER_UP + | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags); + } + + void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); } @@ -923,6 +951,161 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { + sp application = new FakeApplicationHandle(); + + // Create a couple of windows + sp firstWindow = new FakeWindowHandle(application, mDispatcher, + "First Window", ADISPLAY_ID_DEFAULT); + sp secondWindow = new FakeWindowHandle(application, mDispatcher, + "Second Window", ADISPLAY_ID_DEFAULT); + + // Add the windows to the dispatcher + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + + // Send down to the first window + NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&downMotionArgs); + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + + // Transfer touch focus to the second window + mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // The first window gets cancel and the second gets down + firstWindow->consumeMotionCancel(); + secondWindow->consumeMotionDown(); + + // Send up event to the second window + NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets no events and the second gets up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionUp(); +} + +TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { + sp application = new FakeApplicationHandle(); + + PointF touchPoint = {10, 10}; + + // Create a couple of windows + sp firstWindow = new FakeWindowHandle(application, mDispatcher, + "First Window", ADISPLAY_ID_DEFAULT); + sp secondWindow = new FakeWindowHandle(application, mDispatcher, + "Second Window", ADISPLAY_ID_DEFAULT); + + // Add the windows to the dispatcher + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + + // Send down to the first window + NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint}); + mDispatcher->notifyMotion(&downMotionArgs); + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + + // Send pointer down to the first window + NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN + | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}); + mDispatcher->notifyMotion(&pointerDownMotionArgs); + // Only the first window should get the pointer down event + firstWindow->consumeMotionPointerDown(1); + secondWindow->assertNoEvents(); + + // Transfer touch focus to the second window + mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // The first window gets cancel and the second gets down and pointer down + firstWindow->consumeMotionCancel(); + secondWindow->consumeMotionDown(); + secondWindow->consumeMotionPointerDown(1); + + // Send pointer up to the second window + NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP + | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}); + mDispatcher->notifyMotion(&pointerUpMotionArgs); + // The first window gets nothing and the second gets pointer up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionPointerUp(1); + + // Send up event to the second window + NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets nothing and the second gets up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionUp(); +} + +TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { + sp application = new FakeApplicationHandle(); + + // Create a non touch modal window that supports split touch + sp firstWindow = new FakeWindowHandle(application, mDispatcher, + "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->setFrame(Rect(0, 0, 600, 400)); + firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL + | InputWindowInfo::FLAG_SPLIT_TOUCH); + + // Create a non touch modal window that supports split touch + sp secondWindow = new FakeWindowHandle(application, mDispatcher, + "Second Window", ADISPLAY_ID_DEFAULT); + secondWindow->setFrame(Rect(0, 400, 600, 800)); + secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL + | InputWindowInfo::FLAG_SPLIT_TOUCH); + + // Add the windows to the dispatcher + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + + PointF pointInFirst = {300, 200}; + PointF pointInSecond = {300, 600}; + + // Send down to the first window + NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst}); + mDispatcher->notifyMotion(&firstDownMotionArgs); + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + + // Send down to the second window + NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN + | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&secondDownMotionArgs); + // The first window gets a move and the second a down + firstWindow->consumeMotionMove(); + secondWindow->consumeMotionDown(); + + // Transfer touch focus to the second window + mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // The first window gets cancel and the new gets pointer down (it already saw down) + firstWindow->consumeMotionCancel(); + secondWindow->consumeMotionPointerDown(1); + + // Send pointer up to the second window + NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP + | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&pointerUpMotionArgs); + // The first window gets nothing and the second gets pointer up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionPointerUp(1); + + // Send up event to the second window + NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets nothing and the second gets up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionUp(); +} + TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { sp application = new FakeApplicationHandle(); sp window = -- cgit v1.2.3-59-g8ed1b From e0105c9af0572f93980b353c429b71bb6f7ce23b Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 26 Dec 2019 12:32:13 -0800 Subject: Refactor EventHubTests to use UinputDevice EventHubTests uses uinput to emulate input devices. We refactor the system calls into a untility file UinputDevice.h so it can be reused for other integration tests. Test: atest inputflinger_tests Change-Id: If6b1b94b14c0bc71193cbc69ce1239a01990559b --- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/EventHub_test.cpp | 84 ++--------------- services/inputflinger/tests/UinputDevice.cpp | 130 ++++++++++++++++++++++++++ services/inputflinger/tests/UinputDevice.h | 111 ++++++++++++++++++++++ 4 files changed, 252 insertions(+), 74 deletions(-) create mode 100644 services/inputflinger/tests/UinputDevice.cpp create mode 100644 services/inputflinger/tests/UinputDevice.h (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 09ecb13b0a..f913d826bc 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -10,6 +10,7 @@ cc_test { "InputClassifierConverter_test.cpp", "InputDispatcher_test.cpp", "InputReader_test.cpp", + "UinputDevice.cpp", ], cflags: [ "-Wall", diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index 6504738fc7..be2e19e7df 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -16,7 +16,8 @@ #include "EventHub.h" -#include +#include "UinputDevice.h" + #include #include #include @@ -25,16 +26,16 @@ #define TAG "EventHub_test" +using android::createUinputDevice; using android::EventHub; using android::EventHubInterface; using android::InputDeviceIdentifier; using android::RawEvent; using android::sp; -using android::base::StringPrintf; +using android::UinputHomeKey; using std::chrono_literals::operator""ms; static constexpr bool DEBUG = false; -static const char* DEVICE_NAME = "EventHub Test Device"; static void dumpEvents(const std::vector& events) { for (const RawEvent& event : events) { @@ -62,27 +63,26 @@ class EventHubTest : public testing::Test { protected: std::unique_ptr mEventHub; // We are only going to emulate a single input device currently. - android::base::unique_fd mDeviceFd; + std::unique_ptr mKeyboard; int32_t mDeviceId; + virtual void SetUp() override { mEventHub = std::make_unique(); consumeInitialDeviceAddedEvents(); - createDevice(); + mKeyboard = createUinputDevice(); mDeviceId = waitForDeviceCreation(); } virtual void TearDown() override { - mDeviceFd.reset(); + mKeyboard.reset(); waitForDeviceClose(mDeviceId); } - void createDevice(); /** * Return the device id of the created device. */ int32_t waitForDeviceCreation(); void waitForDeviceClose(int32_t deviceId); void consumeInitialDeviceAddedEvents(); - void sendEvent(uint16_t type, uint16_t code, int32_t value); std::vector getEvents(std::chrono::milliseconds timeout = 5ms); }; @@ -105,48 +105,6 @@ std::vector EventHubTest::getEvents(std::chrono::milliseconds timeout) return events; } -void EventHubTest::createDevice() { - mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); - if (mDeviceFd < 0) { - FAIL() << "Can't open /dev/uinput :" << strerror(errno); - } - - /** - * Signal which type of events this input device supports. - * We will emulate a keyboard here. - */ - // enable key press/release event - if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); - } - - // enable set of KEY events - if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno); - } - - // enable synchronization event - if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); - } - - struct uinput_user_dev keyboard = {}; - strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE); - keyboard.id.bustype = BUS_USB; - keyboard.id.vendor = 0x01; - keyboard.id.product = 0x01; - keyboard.id.version = 1; - - if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) { - FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: " - << strerror(errno); - } - - if (ioctl(mDeviceFd, UI_DEV_CREATE)) { - FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno); - } -} - /** * Since the test runs on a real platform, there will be existing devices * in addition to the test devices being added. Therefore, when EventHub is first created, @@ -176,7 +134,7 @@ int32_t EventHubTest::waitForDeviceCreation() { EXPECT_EQ(static_cast(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type); InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId); const int32_t deviceId = deviceAddedEvent.deviceId; - EXPECT_EQ(identifier.name, DEVICE_NAME); + EXPECT_EQ(identifier.name, mKeyboard->getName()); const RawEvent& finishedDeviceScanEvent = events[1]; EXPECT_EQ(static_cast(EventHubInterface::FINISHED_DEVICE_SCAN), finishedDeviceScanEvent.type); @@ -194,22 +152,6 @@ void EventHubTest::waitForDeviceClose(int32_t deviceId) { finishedDeviceScanEvent.type); } -void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) { - struct input_event event = {}; - event.type = type; - event.code = code; - event.value = value; - event.time = {}; // uinput ignores the timestamp - - if (write(mDeviceFd, &event, sizeof(input_event)) < 0) { - std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16 - " with value %" PRId32 " : %s", - type, code, value, strerror(errno)); - ALOGE("%s", msg.c_str()); - ADD_FAILURE() << msg.c_str(); - } -} - /** * Ensure that input_events are generated with monotonic clock. * That means input_event should receive a timestamp that is in the future of the time @@ -218,13 +160,7 @@ void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) { */ TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) { nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC); - // key press - sendEvent(EV_KEY, KEY_HOME, 1); - sendEvent(EV_SYN, SYN_REPORT, 0); - - // key release - sendEvent(EV_KEY, KEY_HOME, 0); - sendEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey()); std::vector events = getEvents(); ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events"; diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp new file mode 100644 index 0000000000..2775d21ce2 --- /dev/null +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UinputDevice.h" + +#include + +namespace android { + +// --- UinputDevice --- + +UinputDevice::UinputDevice(const char* name) : mName(name) {} + +UinputDevice::~UinputDevice() { + if (ioctl(mDeviceFd, UI_DEV_DESTROY)) { + ALOGE("Error while destroying uinput device: %s", strerror(errno)); + } + mDeviceFd.reset(); +} + +void UinputDevice::init() { + mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); + if (mDeviceFd < 0) { + FAIL() << "Can't open /dev/uinput :" << strerror(errno); + } + + struct uinput_user_dev device = {}; + strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE); + device.id.bustype = BUS_USB; + device.id.vendor = 0x01; + device.id.product = 0x01; + device.id.version = 1; + + // Using EXPECT instead of ASSERT to allow the device creation to continue even when + // some failures are reported when configuring the device. + EXPECT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); + + if (write(mDeviceFd, &device, sizeof(device)) < 0) { + FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: " + << strerror(errno); + } + + if (ioctl(mDeviceFd, UI_DEV_CREATE)) { + FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno); + } +} + +void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { + struct input_event event = {}; + event.type = type; + event.code = code; + event.value = value; + event.time = {}; // uinput ignores the timestamp + + if (write(mDeviceFd, &event, sizeof(input_event)) < 0) { + std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16 + " with value %" PRId32 " : %s", + type, code, value, strerror(errno)); + ALOGE("%s", msg.c_str()); + ADD_FAILURE() << msg.c_str(); + } +} + +// --- UinputKeyboard --- + +UinputKeyboard::UinputKeyboard(std::initializer_list keys) + : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {} + +void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { + // enable key press/release event + if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); + } + + // enable set of KEY events + std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) { + if (ioctl(fd, UI_SET_KEYBIT, key)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno); + } + }); + + // enable synchronization event + if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) { + ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); + } +} + +void UinputKeyboard::pressKey(int key) { + if (mKeys.find(key) == mKeys.end()) { + ADD_FAILURE() << mName << ": Cannot inject key press: Key not found: " << key; + } + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 1)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +void UinputKeyboard::releaseKey(int key) { + if (mKeys.find(key) == mKeys.end()) { + ADD_FAILURE() << mName << ": Cannot inject key release: Key not found: " << key; + } + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 0)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +void UinputKeyboard::pressAndReleaseKey(int key) { + EXPECT_NO_FATAL_FAILURE(pressKey(key)); + EXPECT_NO_FATAL_FAILURE(releaseKey(key)); +} + +// --- UinputHomeKey--- + +UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} + +void UinputHomeKey::pressAndReleaseHomeKey() { + EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME)); +} + +} // namespace android diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h new file mode 100644 index 0000000000..57d9011695 --- /dev/null +++ b/services/inputflinger/tests/UinputDevice.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H +#define _UI_TEST_INPUT_UINPUT_INJECTOR_H + +#include +#include +#include +#include +#include + +#include + +namespace android { + +// This is the factory method that must be used to create a UinputDevice. +template +std::unique_ptr createUinputDevice(Ts... args) { + // Using `new` to access non-public constructors. + std::unique_ptr dev(new D(&args...)); + EXPECT_NO_FATAL_FAILURE(dev->init()); + return dev; +} + +// --- UinputDevice --- + +class UinputDevice { +public: + virtual ~UinputDevice(); + + inline const char* getName() const { return mName; } + + // Subclasses must either provide a public constructor or must be-friend the factory method. + template + friend std::unique_ptr createUinputDevice(Ts... args); + +protected: + const char* mName; + + UinputDevice(const char* name); + + // Signals which types of events this device supports before it is created. + // This must be overridden by subclasses. + virtual void configureDevice(int fd, uinput_user_dev* device) = 0; + + void injectEvent(uint16_t type, uint16_t code, int32_t value); + +private: + base::unique_fd mDeviceFd; + + // This is called once by the factory method createUinputDevice(). + void init(); +}; + +// --- UinputKeyboard --- + +class UinputKeyboard : public UinputDevice { +public: + static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device"; + + // Injects key press and sync. + void pressKey(int key); + // Injects key release and sync. + void releaseKey(int key); + // Injects 4 events: key press, sync, key release, and sync. + void pressAndReleaseKey(int key); + + template + friend std::unique_ptr createUinputDevice(Ts... args); + +protected: + UinputKeyboard(std::initializer_list keys = {}); + +private: + void configureDevice(int fd, uinput_user_dev* device) override; + + std::set mKeys; +}; + +// --- UinputHomeKey--- + +// A keyboard device that has a single HOME key. +class UinputHomeKey : public UinputKeyboard { +public: + // Injects 4 events: key press, sync, key release, and sync. + void pressAndReleaseHomeKey(); + + template + friend std::unique_ptr createUinputDevice(Ts... args); + +private: + UinputHomeKey(); +}; + +} // namespace android + +#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H -- cgit v1.2.3-59-g8ed1b From 1aed858b93e4273dd7484c3f3cfef04948abaecb Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Mon, 30 Dec 2019 11:46:51 -0800 Subject: Add InputReader Integration tests The InputReader unit tests rely on internal implementation detail of InputReader, and do not test the threding functionality. We add a new test suite that creates and interacts with InputReader solely through the InputReaderInterface, and also verifies its threading functionality through the start() and stop() methods. These integration tests use uinput to emulate input devices, and emit events, which are verified through the InputListener interface. Bug: 144595884 Test: atest inputflinger_tests Change-Id: If51b35cc46180836e60fd28d50c092396c163034 --- services/inputflinger/tests/InputReader_test.cpp | 150 +++++++++++++++++++++-- 1 file changed, 140 insertions(+), 10 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8ca7e4a893..1da1829d5c 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -18,12 +18,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -187,17 +190,19 @@ public: } void assertInputDevicesChanged() { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); + waitForInputDevices([](bool devicesChanged) { + if (!devicesChanged) { + FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; + } + }); + } - const bool devicesChanged = - mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { - return mInputDevicesChanged; - }); - if (!devicesChanged) { - FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; - } - mInputDevicesChanged = false; + void assertInputDevicesNotChanged() { + waitForInputDevices([](bool devicesChanged) { + if (devicesChanged) { + FAIL() << "Expected notifyInputDevicesChanged() to not be called."; + } + }); } virtual void clearViewports() { @@ -331,6 +336,18 @@ private: virtual std::string getDeviceAlias(const InputDeviceIdentifier&) { return ""; } + + void waitForInputDevices(std::function processDevicesChanged) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool devicesChanged = + mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mInputDevicesChanged; + }); + ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged)); + mInputDevicesChanged = false; + } }; // --- FakeEventHub --- @@ -1658,6 +1675,119 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } +// --- InputReaderIntegrationTest --- + +// These tests create and interact with the InputReader only through its interface. +// The InputReader is started during SetUp(), which starts its processing in its own +// thread. The tests use linux uinput to emulate input devices. +// NOTE: Interacting with the physical device while these tests are running may cause +// the tests to fail. +class InputReaderIntegrationTest : public testing::Test { +protected: + sp mTestListener; + sp mFakePolicy; + sp mReader; + + virtual void SetUp() override { + mFakePolicy = new FakeInputReaderPolicy(); + mTestListener = new TestInputListener(); + + mReader = createInputReader(mFakePolicy, mTestListener); + ASSERT_EQ(mReader->start(), OK); + + // Since this test is run on a real device, all the input devices connected + // to the test device will show up in mReader. We wait for those input devices to + // show up before beginning the tests. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + } + + virtual void TearDown() override { + ASSERT_EQ(mReader->stop(), OK); + mTestListener.clear(); + mFakePolicy.clear(); + } +}; + +TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { + // An invalid input device that is only used for this test. + class InvalidUinputDevice : public UinputDevice { + public: + InvalidUinputDevice() : UinputDevice("Invalid Device") {} + + private: + void configureDevice(int fd, uinput_user_dev* device) override {} + }; + + const size_t numDevices = mFakePolicy->getInputDevices().size(); + + // UinputDevice does not set any event or key bits, so InputReader should not + // consider it as a valid device. + std::unique_ptr invalidDevice = createUinputDevice(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled()); + ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size()); + + invalidDevice.reset(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled()); + ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size()); +} + +TEST_F(InputReaderIntegrationTest, AddNewDevice) { + const size_t initialNumDevices = mFakePolicy->getInputDevices().size(); + + std::unique_ptr keyboard = createUinputDevice(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); + + // Find the test device by its name. + std::vector inputDevices; + mReader->getInputDevices(inputDevices); + InputDeviceInfo* keyboardInfo = nullptr; + const char* keyboardName = keyboard->getName(); + for (unsigned int i = 0; i < initialNumDevices + 1; i++) { + if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) { + keyboardInfo = &inputDevices[i]; + break; + } + } + ASSERT_NE(keyboardInfo, nullptr); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources()); + ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size()); + + keyboard.reset(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size()); +} + +TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { + std::unique_ptr keyboard = createUinputDevice(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + + NotifyConfigurationChangedArgs configChangedArgs; + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs)); + uint32_t prevSequenceNum = configChangedArgs.sequenceNum; + nsecs_t prevTimestamp = configChangedArgs.eventTime; + + NotifyKeyArgs keyArgs; + keyboard->pressAndReleaseHomeKey(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum); + prevSequenceNum = keyArgs.sequenceNum; + ASSERT_LE(prevTimestamp, keyArgs.eventTime); + prevTimestamp = keyArgs.eventTime; + + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum); + ASSERT_LE(prevTimestamp, keyArgs.eventTime); +} // --- InputDeviceTest --- -- cgit v1.2.3-59-g8ed1b From 54d3e189009e4138ec5074b0ac79f56ce322daed Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 15 Jan 2020 17:38:38 -0800 Subject: Add verifyInputEvent api to InputDispatcher Now InputDispatcher will be able to check whether a certain InputEvent is legitimate. Use the 'verifyInputEvent' api to determine if a given 'InputEvent' actually came from InputDispatcher. Bug: 134977432 Test: atest VerifiedKeyEventTest VerifiedMotionEventTest libinput_tests inputflinger_tests Change-Id: I8e7fa9bfa3c14b0b0d949fb5e28b43ff7583398f --- include/input/Input.h | 62 +++++++++++ libs/input/Input.cpp | 24 +++++ libs/input/tests/Android.bp | 2 +- libs/input/tests/InputEvent_test.cpp | 1 - .../input/tests/InputPublisherAndConsumer_test.cpp | 1 + libs/input/tests/StructLayout_test.cpp | 30 ++++++ libs/input/tests/VerifiedInputEvent_test.cpp | 116 +++++++++++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 4 + services/inputflinger/dispatcher/InputDispatcher.h | 2 + .../dispatcher/include/InputDispatcherInterface.h | 7 ++ 10 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 libs/input/tests/VerifiedInputEvent_test.cpp (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index cf0814cf2f..14a7288d19 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -73,6 +73,19 @@ enum { AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; +/** + * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified. + * These values must be kept in sync with VerifiedKeyEvent.java + */ +constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED; + +/** + * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified. + * These values must be kept in sync with VerifiedMotionEvent.java + */ +constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + enum { /* Used when a motion event is not associated with any display. * Typically used for non-pointer events. */ @@ -718,6 +731,55 @@ protected: bool mInTouchMode; }; +/** + * Base class for verified events. + * Do not create a VerifiedInputEvent explicitly. + * Use helper functions to create them from InputEvents. + */ +struct __attribute__((__packed__)) VerifiedInputEvent { + enum class Type : int32_t { + KEY = AINPUT_EVENT_TYPE_KEY, + MOTION = AINPUT_EVENT_TYPE_MOTION, + }; + + Type type; + int32_t deviceId; + nsecs_t eventTimeNanos; + uint32_t source; + int32_t displayId; +}; + +/** + * Same as KeyEvent, but only contains the data that can be verified. + * If you update this class, you must also update VerifiedKeyEvent.java + */ +struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent { + int32_t action; + nsecs_t downTimeNanos; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; +}; + +/** + * Same as MotionEvent, but only contains the data that can be verified. + * If you update this class, you must also update VerifiedMotionEvent.java + */ +struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEvent { + float rawX; + float rawY; + int32_t actionMasked; + nsecs_t downTimeNanos; + int32_t flags; + int32_t metaState; + int32_t buttonState; +}; + +VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event); +VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event); + /* * Input event factory. */ diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 85b0fd0ec7..2a73dc0149 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,6 +57,30 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } +VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { + return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), + event.getSource(), event.getDisplayId()}, + event.getAction(), + event.getDownTime(), + event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, + event.getKeyCode(), + event.getScanCode(), + event.getMetaState(), + event.getRepeatCount()}; +} + +VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) { + return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(), + event.getSource(), event.getDisplayId()}, + event.getRawX(0), + event.getRawY(0), + event.getActionMasked(), + event.getDownTime(), + event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, + event.getMetaState(), + event.getButtonState()}; +} + void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { mDeviceId = deviceId; diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index c1c35e1b89..fb21d5e3b1 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -10,12 +10,12 @@ cc_test { "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", + "VerifiedInputEvent_test.cpp", ], cflags: [ "-Wall", "-Wextra", "-Werror", - "-Wno-unused-variable", ], shared_libs: [ "libinput", diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index dce1f29124..d0f761887a 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -46,7 +46,6 @@ TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { } TEST_F(PointerCoordsTest, AxisValues) { - float* valuePtr; PointerCoords coords; coords.clear(); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index d4bbf6c6ac..885196f3f3 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -38,6 +38,7 @@ protected: virtual void SetUp() { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result); mPublisher = new InputPublisher(serverChannel); mConsumer = new InputConsumer(clientChannel); diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index aa8a2d488f..dd127fcabd 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -98,4 +98,34 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Focus) == 8); } +// --- VerifiedInputEvent --- +// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed. +// We will treat them as byte collections when signing them. There should not be any uninitialized +// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications +// will fail. + +void TestVerifiedEventSize() { + // VerifiedInputEvent + constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) + + sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) + + sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId); + static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE); + + // VerifiedKeyEvent + constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE + + sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) + + sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) + + sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) + + sizeof(VerifiedKeyEvent::repeatCount); + static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE); + + // VerifiedMotionEvent + constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE + + sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) + + sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) + + sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) + + sizeof(VerifiedMotionEvent::buttonState); + static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE); +} + } // namespace android diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp new file mode 100644 index 0000000000..a59dbe5987 --- /dev/null +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace android { + +static KeyEvent getKeyEventWithFlags(int32_t flags) { + KeyEvent event; + event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/, + AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/); + return event; +} + +static MotionEvent getMotionEventWithFlags(int32_t flags) { + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, + 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, + 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); + return event; +} + +TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) { + KeyEvent event = getKeyEventWithFlags(0); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + + ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type); + + ASSERT_EQ(event.getDeviceId(), verified.deviceId); + ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos); + ASSERT_EQ(event.getSource(), verified.source); + ASSERT_EQ(event.getDisplayId(), verified.displayId); + + ASSERT_EQ(event.getAction(), verified.action); + ASSERT_EQ(event.getDownTime(), verified.downTimeNanos); + ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags); + ASSERT_EQ(event.getKeyCode(), verified.keyCode); + ASSERT_EQ(event.getScanCode(), verified.scanCode); + ASSERT_EQ(event.getMetaState(), verified.metaState); + ASSERT_EQ(event.getRepeatCount(), verified.repeatCount); +} + +TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) { + KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags); +} + +TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) { + KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION); + VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event); + ASSERT_EQ(0, verified.flags); +} + +TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) { + MotionEvent event = getMotionEventWithFlags(0); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + + ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type); + + ASSERT_EQ(event.getDeviceId(), verified.deviceId); + ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos); + ASSERT_EQ(event.getSource(), verified.source); + ASSERT_EQ(event.getDisplayId(), verified.displayId); + + ASSERT_EQ(event.getRawX(0), verified.rawX); + ASSERT_EQ(event.getRawY(0), verified.rawY); + ASSERT_EQ(event.getAction(), verified.actionMasked); + ASSERT_EQ(event.getDownTime(), verified.downTimeNanos); + ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags); + ASSERT_EQ(event.getMetaState(), verified.metaState); + ASSERT_EQ(event.getButtonState(), verified.buttonState); +} + +TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) { + MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags); +} + +TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) { + MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED); + VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event); + ASSERT_EQ(0, verified.flags); +} + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f2b95e7f7a..a8158ba3cf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3340,6 +3340,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec return injectionResult; } +std::unique_ptr InputDispatcher::verifyInputEvent(const InputEvent& event) { + return nullptr; +} + bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { return injectorUid == 0 || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d2aea80069..72511e9bf2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -96,6 +96,8 @@ public: int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) override; + virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; + virtual void setInputWindows( const std::vector>& inputWindowHandles, int32_t displayId, const sp& setInputWindowsListener = nullptr) override; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 3424f4ce84..6e986768fc 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -92,6 +92,13 @@ public: int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) = 0; + /* + * Check whether InputEvent actually happened by checking the signature of the event. + * + * Return nullptr if the event cannot be verified. + */ + virtual std::unique_ptr verifyInputEvent(const InputEvent& event) = 0; + /* Sets the list of input windows. * * This method may be called on any thread (usually by the input manager). -- cgit v1.2.3-59-g8ed1b From 342c9270b18499de563d6997bd3b08275b60982e Mon Sep 17 00:00:00 2001 From: Gang Wang Date: Mon, 13 Jan 2020 13:15:04 -0500 Subject: Add HmacKeyManager to InputDispatcher This class will be used to sign pieces of MotionEvent and MotionEntry. Bug: 134977432 Test: atest libinput_tests inputflinger_tests Change-Id: Ica006edc5b049e1d5d894de255866eedb9cbb0e2 --- services/inputflinger/Android.bp | 1 + services/inputflinger/dispatcher/Android.bp | 1 + .../inputflinger/dispatcher/InputDispatcher.cpp | 51 +++++++++++++++ services/inputflinger/dispatcher/InputDispatcher.h | 10 +++ .../inputflinger/tests/InputDispatcher_test.cpp | 75 ++++++++++++++++++++++ 5 files changed, 138 insertions(+) (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 308e93aa48..5c80d551b8 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -40,6 +40,7 @@ cc_library_shared { "libinputreporter", "libinputreader", "libbinder", + "libcrypto", "libcutils", "libhidlbase", "libinput", diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index a556aad553..3f956a88fb 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -28,6 +28,7 @@ cc_library_static { ], shared_libs: [ "libbase", + "libcrypto", "libcutils", "libinput", "libinputreporter", diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a8158ba3cf..306ed40abb 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -61,6 +61,8 @@ static constexpr bool DEBUG_FOCUS = false; #include #include #include +#include +#include #include #include @@ -325,6 +327,55 @@ static std::unique_ptr createDispatchEntry(const InputTarget& inp return dispatchEntry; } +static std::array getRandomKey() { + std::array key; + if (RAND_bytes(key.data(), key.size()) != 1) { + LOG_ALWAYS_FATAL("Can't generate HMAC key"); + } + return key; +} + +// --- HmacKeyManager --- + +HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {} + +std::array HmacKeyManager::sign(const VerifiedInputEvent& event) const { + size_t size; + switch (event.type) { + case VerifiedInputEvent::Type::KEY: { + size = sizeof(VerifiedKeyEvent); + break; + } + case VerifiedInputEvent::Type::MOTION: { + size = sizeof(VerifiedMotionEvent); + break; + } + } + std::vector data; + const uint8_t* start = reinterpret_cast(&event); + data.assign(start, start + size); + return sign(data); +} + +std::array HmacKeyManager::sign(const std::vector& data) const { + // SHA256 always generates 32-bytes result + std::array hash; + unsigned int hashLen = 0; + uint8_t* result = HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data.data(), data.size(), + hash.data(), &hashLen); + if (result == nullptr) { + ALOGE("Could not sign the data using HMAC"); + return INVALID_HMAC; + } + + if (hashLen != hash.size()) { + ALOGE("HMAC-SHA256 has unexpected length"); + return INVALID_HMAC; + } + + return hash; +} + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp& policy) diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 72511e9bf2..ded59a595c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -56,6 +56,16 @@ namespace android::inputdispatcher { class Connection; +class HmacKeyManager { +public: + HmacKeyManager(); + std::array sign(const VerifiedInputEvent& event) const; + +private: + std::array sign(const std::vector& data) const; + const std::array mHmacKey; +}; + /* Dispatches events to input targets. Some functions of the input dispatcher, such as * identifying input targets, are controlled by a separate policy object. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 27db8f5534..c4092cd696 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -43,6 +43,18 @@ struct PointF { float y; }; +/** + * Return a DOWN key event with KEYCODE_A. + */ +static KeyEvent getTestKeyEvent() { + KeyEvent event; + + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, + ARBITRARY_TIME); + return event; +} + // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { @@ -197,6 +209,69 @@ private: } }; +// --- HmacKeyManagerTest --- + +class HmacKeyManagerTest : public testing::Test { +protected: + HmacKeyManager mHmacKeyManager; +}; + +/** + * Ensure that separate calls to sign the same data are generating the same key. + * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance + * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky + * tests. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) { + KeyEvent event = getTestKeyEvent(); + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); + + std::array hmac1 = mHmacKeyManager.sign(verifiedEvent); + std::array hmac2 = mHmacKeyManager.sign(verifiedEvent); + ASSERT_EQ(hmac1, hmac2); +} + +/** + * Ensure that changes in VerifiedKeyEvent produce a different hmac. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) { + KeyEvent event = getTestKeyEvent(); + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); + std::array initialHmac = mHmacKeyManager.sign(verifiedEvent); + + verifiedEvent.deviceId += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.source += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.eventTimeNanos += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.displayId += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.action += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.downTimeNanos += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.flags += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.keyCode += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.scanCode += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.metaState += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); + + verifiedEvent.repeatCount += 1; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); +} // --- InputDispatcherTest --- -- cgit v1.2.3-59-g8ed1b From 0d8ed6e8ade20652cea20f579f40b1d698ce8fc0 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 17 Jan 2020 15:48:59 -0800 Subject: Use VIRTUAL_KEYBOARD_ID for injected events The events injected via injectInputEvent will no longer be allowed to specify an arbitrary device id. All injected events will now come from VIRTUAL_KEYBOARD_ID. Bug: 134977432 Test: atest libinput_tests inputflinger_tests Change-Id: I9a61a99cf5f8ca1a27e4526dd6feedf2c1beec0f --- .../inputflinger/dispatcher/InputDispatcher.cpp | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 306ed40abb..75bc0aa7c8 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -60,6 +60,7 @@ static constexpr bool DEBUG_FOCUS = false; #include #include #include +#include #include #include #include @@ -3204,22 +3205,22 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec std::queue injectedEntries; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { - KeyEvent keyEvent; - keyEvent.initialize(*static_cast(event)); - int32_t action = keyEvent.getAction(); + const KeyEvent& incomingKey = static_cast(*event); + int32_t action = incomingKey.getAction(); if (!validateKeyEvent(action)) { return INPUT_EVENT_INJECTION_FAILED; } - int32_t flags = keyEvent.getFlags(); - int32_t keyCode = keyEvent.getKeyCode(); - int32_t metaState = keyEvent.getMetaState(); - accelerateMetaShortcuts(keyEvent.getDeviceId(), action, + int32_t flags = incomingKey.getFlags(); + int32_t keyCode = incomingKey.getKeyCode(); + int32_t metaState = incomingKey.getMetaState(); + accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action, /*byref*/ keyCode, /*byref*/ metaState); - keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), - keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode, - keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), - keyEvent.getDownTime(), keyEvent.getEventTime()); + KeyEvent keyEvent; + keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode, + incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), + incomingKey.getDownTime(), incomingKey.getEventTime()); if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { policyFlags |= POLICY_FLAG_VIRTUAL; @@ -3237,11 +3238,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); KeyEntry* injectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - keyEvent.getDeviceId(), keyEvent.getSource(), - keyEvent.getDisplayId(), policyFlags, action, flags, - keyEvent.getKeyCode(), keyEvent.getScanCode(), - keyEvent.getMetaState(), keyEvent.getRepeatCount(), - keyEvent.getDownTime()); + VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(), + policyFlags, action, flags, keyEvent.getKeyCode(), + keyEvent.getScanCode(), keyEvent.getMetaState(), + keyEvent.getRepeatCount(), keyEvent.getDownTime()); injectedEntries.push(injectedEntry); break; } @@ -3272,7 +3272,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); MotionEntry* injectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), + VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getClassification(), @@ -3289,7 +3289,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec samplePointerCoords += pointerCount; MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), + VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), -- cgit v1.2.3-59-g8ed1b From f4916efbbe6272362c6cabe339a81e394697587f Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Tue, 14 Jan 2020 11:57:18 -0800 Subject: Refactor InputMapper creation and collection type Move the creation of InputMappers from InputReader into InputDevice and change the collection type from a vector of raw pointers to a vector of unique_ptrs. Add helper functions for iterating over the mappers data structure. InputDevice::addMapper(...) is preserved for test cases, except rather than taking a bare pointer to a mapper, it creates the mapper, adds it's mapper vector, and returns a reference to this mapper. The unit tests have been updated for this change. Test: atest inputflinger_tests libinput_tests Change-Id: I1e1b69e8bd13dcfa835b2f846fd5cf0d6a4e1719 --- services/inputflinger/reader/InputDevice.cpp | 178 +++--- services/inputflinger/reader/InputReader.cpp | 92 +-- services/inputflinger/reader/include/InputDevice.h | 32 +- services/inputflinger/tests/InputReader_test.cpp | 626 +++++++++------------ 4 files changed, 422 insertions(+), 506 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 7fed61f09d..1c08ab1ed2 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -18,7 +18,16 @@ #include "InputDevice.h" -#include "InputMapper.h" +#include "CursorInputMapper.h" +#include "ExternalStylusInputMapper.h" +#include "InputReaderContext.h" +#include "JoystickInputMapper.h" +#include "KeyboardInputMapper.h" +#include "MultiTouchInputMapper.h" +#include "RotaryEncoderInputMapper.h" +#include "SingleTouchInputMapper.h" +#include "SwitchInputMapper.h" +#include "VibratorInputMapper.h" namespace android { @@ -36,13 +45,7 @@ InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t genera mHasMic(false), mDropUntilNextSync(false) {} -InputDevice::~InputDevice() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - delete mMappers[i]; - } - mMappers.clear(); -} +InputDevice::~InputDevice() {} bool InputDevice::isEnabled() { return getEventHub()->isDeviceEnabled(mId); @@ -110,15 +113,80 @@ void InputDevice::dump(std::string& dump) { } } - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } + for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); }); } -void InputDevice::addMapper(InputMapper* mapper) { - mMappers.push_back(mapper); +void InputDevice::populateMappers() { + uint32_t classes = mClasses; + std::vector>& mappers = mMappers; + + // External devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { + setExternal(true); + } + + // Devices with mics. + if (classes & INPUT_DEVICE_CLASS_MIC) { + setMic(true); + } + + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + mappers.push_back(std::make_unique(this)); + } + + // Scroll wheel-like devices. + if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { + mappers.push_back(std::make_unique(this)); + } + + // Vibrator-like devices. + if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { + mappers.push_back(std::make_unique(this)); + } + + // Keyboard-like devices. + uint32_t keyboardSource = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSource |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSource |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSource |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSource != 0) { + mappers.push_back( + std::make_unique(this, keyboardSource, keyboardType)); + } + + // Cursor-like devices. + if (classes & INPUT_DEVICE_CLASS_CURSOR) { + mappers.push_back(std::make_unique(this)); + } + + // Touchscreens and touchpad devices. + if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { + mappers.push_back(std::make_unique(this)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { + mappers.push_back(std::make_unique(this)); + } + + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + mappers.push_back(std::make_unique(this)); + } + + // External stylus-like devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + mappers.push_back(std::make_unique(this)); + } } void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, @@ -193,10 +261,10 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } } - for (InputMapper* mapper : mMappers) { - mapper->configure(when, config, changes); - mSources |= mapper->getSources(); - } + for_each_mapper([this, when, config, changes](InputMapper& mapper) { + mapper.configure(when, config, changes); + mSources |= mapper.getSources(); + }); // If a device is just plugged but it might be disabled, we need to update some info like // axis range of touch from each InputMapper first, then disable it. @@ -207,9 +275,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } void InputDevice::reset(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->reset(when); - } + for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); }); mContext->updateGlobalMetaState(); @@ -244,32 +310,25 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { mDropUntilNextSync = true; reset(rawEvent->when); } else { - for (InputMapper* mapper : mMappers) { - mapper->process(rawEvent); - } + for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); }); } --count; } } void InputDevice::timeoutExpired(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->timeoutExpired(when); - } + for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); }); } void InputDevice::updateExternalStylusState(const StylusState& state) { - for (InputMapper* mapper : mMappers) { - mapper->updateExternalStylusState(state); - } + for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); }); } void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, mHasMic); - for (InputMapper* mapper : mMappers) { - mapper->populateDeviceInfo(outDeviceInfo); - } + for_each_mapper( + [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); }); } int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { @@ -286,11 +345,12 @@ int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + for (auto& mapperPtr : mMappers) { + InputMapper& mapper = *mapperPtr; + if (sourcesMatchMask(mapper.getSources(), sourceMask)) { // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); + int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code); if (currentResult >= AKEY_STATE_DOWN) { return currentResult; } else if (currentResult == AKEY_STATE_UP) { @@ -304,51 +364,41 @@ int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc ge bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { bool result = false; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + for_each_mapper([&result, sourceMask, numCodes, keyCodes, outFlags](InputMapper& mapper) { + if (sourcesMatchMask(mapper.getSources(), sourceMask)) { + result |= mapper.markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } - } + }); return result; } void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->vibrate(pattern, patternSize, repeat, token); - } + for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) { + mapper.vibrate(pattern, patternSize, repeat, token); + }); } void InputDevice::cancelVibrate(int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->cancelVibrate(token); - } + for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); }); } void InputDevice::cancelTouch(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->cancelTouch(when); - } + for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); }); } int32_t InputDevice::getMetaState() { int32_t result = 0; - for (InputMapper* mapper : mMappers) { - result |= mapper->getMetaState(); - } + for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); }); return result; } void InputDevice::updateMetaState(int32_t keyCode) { - for (InputMapper* mapper : mMappers) { - mapper->updateMetaState(keyCode); - } + for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); }); } void InputDevice::fadePointer() { - for (InputMapper* mapper : mMappers) { - mapper->fadePointer(); - } + for_each_mapper([](InputMapper& mapper) { mapper.fadePointer(); }); } void InputDevice::bumpGeneration() { @@ -367,14 +417,8 @@ std::optional InputDevice::getAssociatedDisplayId() { } // No associated display port, check if some InputMapper is associated. - for (InputMapper* mapper : mMappers) { - std::optional associatedDisplayId = mapper->getAssociatedDisplayId(); - if (associatedDisplayId) { - return associatedDisplayId; - } - } - - return std::nullopt; + return first_in_mappers( + [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); }); } } // namespace android diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 2023c6e8da..010729a6d3 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -18,33 +18,22 @@ #include "InputReader.h" -#include "CursorInputMapper.h" -#include "ExternalStylusInputMapper.h" -#include "InputReaderContext.h" -#include "JoystickInputMapper.h" -#include "KeyboardInputMapper.h" -#include "MultiTouchInputMapper.h" -#include "RotaryEncoderInputMapper.h" -#include "SingleTouchInputMapper.h" -#include "SwitchInputMapper.h" -#include "VibratorInputMapper.h" - +#include #include +#include +#include #include #include +#include #include #include #include #include - -#include #include - -#include -#include -#include #include +#include "InputDevice.h" + using android::base::StringPrintf; namespace android { @@ -261,74 +250,7 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controlle uint32_t classes) { InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), controllerNumber, identifier, classes); - - // External devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { - device->setExternal(true); - } - - // Devices with mics. - if (classes & INPUT_DEVICE_CLASS_MIC) { - device->setMic(true); - } - - // Switch-like devices. - if (classes & INPUT_DEVICE_CLASS_SWITCH) { - device->addMapper(new SwitchInputMapper(device)); - } - - // Scroll wheel-like devices. - if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { - device->addMapper(new RotaryEncoderInputMapper(device)); - } - - // Vibrator-like devices. - if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { - device->addMapper(new VibratorInputMapper(device)); - } - - // Keyboard-like devices. - uint32_t keyboardSource = 0; - int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; - if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { - keyboardSource |= AINPUT_SOURCE_KEYBOARD; - } - if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; - } - if (classes & INPUT_DEVICE_CLASS_DPAD) { - keyboardSource |= AINPUT_SOURCE_DPAD; - } - if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { - keyboardSource |= AINPUT_SOURCE_GAMEPAD; - } - - if (keyboardSource != 0) { - device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); - } - - // Cursor-like devices. - if (classes & INPUT_DEVICE_CLASS_CURSOR) { - device->addMapper(new CursorInputMapper(device)); - } - - // Touchscreens and touchpad devices. - if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { - device->addMapper(new MultiTouchInputMapper(device)); - } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { - device->addMapper(new SingleTouchInputMapper(device)); - } - - // Joystick-like devices. - if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { - device->addMapper(new JoystickInputMapper(device)); - } - - // External stylus-like devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - device->addMapper(new ExternalStylusInputMapper(device)); - } - + device->populateMappers(); return device; } diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 882407dd20..d06cc20719 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -67,7 +67,7 @@ public: void setEnabled(bool enabled, nsecs_t when); void dump(std::string& dump); - void addMapper(InputMapper* mapper); + void populateMappers(); void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); void reset(nsecs_t when); void process(const RawEvent* rawEvents, size_t count); @@ -116,6 +116,14 @@ public: std::optional getAssociatedDisplayId(); + // construct and add a mapper to the input device + template + T& addMapper(Args... args) { + T* mapper = new T(this, args...); + mMappers.emplace_back(mapper); + return *mapper; + } + private: InputReaderContext* mContext; int32_t mId; @@ -125,7 +133,7 @@ private: std::string mAlias; uint32_t mClasses; - std::vector mMappers; + std::vector> mMappers; uint32_t mSources; bool mIsExternal; @@ -138,6 +146,26 @@ private: int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); PropertyMap mConfiguration; + + // run a function against every mapper + inline void for_each_mapper(std::function f) { + for (auto& mapperPtr : mMappers) { + f(*mapperPtr); + } + } + + // return the first value returned by a function over every mapper. + // if all mappers return nullopt, return nullopt. + template + inline std::optional first_in_mappers(std::function(InputMapper&)> f) { + for (auto& mapperPtr : mMappers) { + std::optional ret = f(*mapperPtr); + if (ret) { + return ret; + } + } + return std::nullopt; + } }; } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8ca7e4a893..7cd8793d5a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1354,22 +1354,22 @@ protected: ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } - void disableDevice(int32_t deviceId, InputDevice* device) { + void disableDevice(int32_t deviceId) { mFakePolicy->addDisabledDevice(deviceId); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } - void enableDevice(int32_t deviceId, InputDevice* device) { + void enableDevice(int32_t deviceId) { mFakePolicy->removeDisabledDevice(deviceId); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } - FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, - const std::string& name, uint32_t classes, uint32_t sources, - const PropertyMap* configuration) { + FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, + const std::string& name, uint32_t classes, + uint32_t sources, + const PropertyMap* configuration) { InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes); - FakeInputMapper* mapper = new FakeInputMapper(device, sources); - device->addMapper(mapper); + FakeInputMapper& mapper = device->addMapper(sources); mReader->setNextDevice(device); addDevice(deviceId, name, classes, configuration); return mapper; @@ -1406,8 +1406,7 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); // Must add at least one mapper or the device will be ignored! - FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD); - device->addMapper(mapper); + device->addMapper(AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); @@ -1418,20 +1417,20 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), true); - disableDevice(deviceId, device); + disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(deviceId, resetArgs.deviceId); ASSERT_EQ(device->isEnabled(), false); - disableDevice(deviceId, device); + disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled()); ASSERT_EQ(device->isEnabled(), false); - enableDevice(deviceId, device); + enableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(deviceId, resetArgs.deviceId); @@ -1439,10 +1438,10 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { } TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = nullptr; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); - mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); + FakeInputMapper& mapper = + addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); + mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0, AINPUT_SOURCE_ANY, AKEYCODE_A)) @@ -1466,10 +1465,10 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = nullptr; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); - mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN); + FakeInputMapper& mapper = + addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); + mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0, AINPUT_SOURCE_ANY, KEY_A)) @@ -1493,10 +1492,10 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = nullptr; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); - mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN); + FakeInputMapper& mapper = + addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); + mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0, AINPUT_SOURCE_ANY, SW_LID)) @@ -1520,12 +1519,12 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = nullptr; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); + FakeInputMapper& mapper = + addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); - mapper->addSupportedKeyCode(AKEYCODE_A); - mapper->addSupportedKeyCode(AKEYCODE_B); + mapper.addSupportedKeyCode(AKEYCODE_A); + mapper.addSupportedKeyCode(AKEYCODE_B); const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; uint8_t flags[4] = { 0, 0, 0, 1 }; @@ -1565,16 +1564,16 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange } TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { - FakeInputMapper* mapper = nullptr; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); + FakeInputMapper& mapper = + addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + AINPUT_SOURCE_KEYBOARD, nullptr); mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; - ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event)); + ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event)); ASSERT_EQ(0, event.when); ASSERT_EQ(1, event.deviceId); ASSERT_EQ(EV_KEY, event.type); @@ -1587,8 +1586,7 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); // Must add at least one mapper or the device will be ignored! - FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD); - device->addMapper(mapper); + device->addMapper(AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); @@ -1596,19 +1594,19 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); uint32_t prevSequenceNum = resetArgs.sequenceNum; - disableDevice(deviceId, device); + disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; - enableDevice(deviceId, device); + enableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); prevSequenceNum = resetArgs.sequenceNum; - disableDevice(deviceId, device); + disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); @@ -1621,8 +1619,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { const char* DEVICE_LOCATION = "USB1"; InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass, DEVICE_LOCATION); - FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN); - device->addMapper(mapper); + FakeInputMapper& mapper = device->addMapper(AINPUT_SOURCE_TOUCHSCREEN); mReader->setNextDevice(device); const uint8_t hdmi1 = 1; @@ -1645,7 +1642,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); @@ -1653,14 +1650,13 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); // Can't dispatch event from a disabled device. - disableDevice(deviceId, device); + disableDevice(deviceId); mReader->loopOnce(); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } // --- InputDeviceTest --- - class InputDeviceTest : public testing::Test { protected: static const char* DEVICE_NAME; @@ -1764,21 +1760,19 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // Configuration. mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value")); - FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD); - mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mapper1->setMetaState(AMETA_ALT_ON); - mapper1->addSupportedKeyCode(AKEYCODE_A); - mapper1->addSupportedKeyCode(AKEYCODE_B); - mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); - mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP); - mapper1->setScanCodeState(2, AKEY_STATE_DOWN); - mapper1->setScanCodeState(3, AKEY_STATE_UP); - mapper1->setSwitchState(4, AKEY_STATE_DOWN); - mDevice->addMapper(mapper1); - - FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); - mapper2->setMetaState(AMETA_SHIFT_ON); - mDevice->addMapper(mapper2); + FakeInputMapper& mapper1 = mDevice->addMapper(AINPUT_SOURCE_KEYBOARD); + mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); + mapper1.setMetaState(AMETA_ALT_ON); + mapper1.addSupportedKeyCode(AKEYCODE_A); + mapper1.addSupportedKeyCode(AKEYCODE_B); + mapper1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); + mapper1.setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP); + mapper1.setScanCodeState(2, AKEY_STATE_DOWN); + mapper1.setScanCodeState(3, AKEY_STATE_UP); + mapper1.setSwitchState(4, AKEY_STATE_DOWN); + + FakeInputMapper& mapper2 = mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + mapper2.setMetaState(AMETA_SHIFT_ON); InputReaderConfiguration config; mDevice->configure(ARBITRARY_TIME, &config, 0); @@ -1788,13 +1782,13 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe << "Device should have read configuration during configuration phase."; ASSERT_STREQ("value", propertyValue.string()); - ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled()); // Reset mDevice->reset(ARBITRARY_TIME); - ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled()); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -1850,16 +1844,15 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe RawEvent event; mDevice->process(&event, 1); - ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled()); + ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); } // A single input device is associated with a specific display. Check that: // 1. Device is disabled if the viewport corresponding to the associated display is not found // 2. Device is disabled when setEnabled API is called TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { - FakeInputMapper* mapper = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); - mDevice->addMapper(mapper); + mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); // First Configuration. mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); @@ -1944,10 +1937,12 @@ protected: mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); } - void addMapperAndConfigure(InputMapper* mapper) { - mDevice->addMapper(mapper); + template + T& addMapperAndConfigure(Args... args) { + T& mapper = mDevice->addMapper(args...); configureDevice(0); mDevice->reset(ARBITRARY_TIME); + return mapper; } void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, @@ -1962,15 +1957,15 @@ protected: mFakePolicy->clearViewports(); } - static void process(InputMapper* mapper, nsecs_t when, int32_t type, - int32_t code, int32_t value) { + static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, + int32_t value) { RawEvent event; event.when = when; - event.deviceId = mapper->getDeviceId(); + event.deviceId = mapper.getDeviceId(); event.type = type; event.code = code; event.value = value; - mapper->process(&event); + mapper.process(&event); } static void assertMotionRange(const InputDeviceInfo& info, @@ -2024,26 +2019,23 @@ protected: }; TEST_F(SwitchInputMapperTest, GetSources) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); + SwitchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources()); + ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources()); } TEST_F(SwitchInputMapperTest, GetSwitchState) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); + SwitchInputMapper& mapper = addMapperAndConfigure(); mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1); - ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); + ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0); - ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); + ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); } TEST_F(SwitchInputMapperTest, Process) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); + SwitchInputMapper& mapper = addMapperAndConfigure(); process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1); process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1); @@ -2068,7 +2060,7 @@ protected: void prepareDisplay(int32_t orientation); - void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode, + void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode, int32_t displayId = ADISPLAY_ID_NONE); }; @@ -2081,7 +2073,7 @@ void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL); } -void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, +void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode, int32_t displayId) { NotifyKeyArgs args; @@ -2102,11 +2094,11 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, } TEST_F(KeyboardInputMapperTest, GetSources) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources()); } TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { @@ -2115,9 +2107,9 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); // Key down by scan code. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); @@ -2213,38 +2205,38 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); // Initial metastate. - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); // Metakey down. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1); NotifyKeyArgs args; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); // Key down. process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); // Key up. process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); // Metakey up. process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); } @@ -2254,9 +2246,9 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); prepareDisplay(DISPLAY_ORIENTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -2275,10 +2267,10 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addConfigurationProperty("keyboard.orientationAware", "1"); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); prepareDisplay(DISPLAY_ORIENTATION_0); ASSERT_NO_FATAL_FAILURE( @@ -2348,9 +2340,9 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware // key events should not be associated with a specific display id mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); NotifyKeyArgs args; // Display id should be ADISPLAY_ID_NONE without any display configuration. @@ -2373,10 +2365,10 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { // key events should be associated with the internal viewport mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addConfigurationProperty("keyboard.orientationAware", "1"); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); NotifyKeyArgs args; // Display id should be ADISPLAY_ID_NONE without any display configuration. @@ -2402,39 +2394,39 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { } TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1); - ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); + ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0); - ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); + ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); } TEST_F(KeyboardInputMapperTest, GetScanCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1); - ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); + ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0); - ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); + ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); } TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags)); + ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags)); ASSERT_TRUE(flags[0]); ASSERT_FALSE(flags[1]); } @@ -2447,9 +2439,9 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); // Initialization should have turned all of the lights off. ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); @@ -2462,7 +2454,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); // Toggle num lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); @@ -2470,7 +2462,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle caps lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); @@ -2478,7 +2470,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); @@ -2486,7 +2478,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle num lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); @@ -2494,7 +2486,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState()); + ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); @@ -2502,7 +2494,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); } TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { @@ -2527,13 +2519,13 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); - KeyboardInputMapper* mapper2 = new KeyboardInputMapper(device2.get(), AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); - device2->addMapper(mapper2); + KeyboardInputMapper& mapper2 = + device2->addMapper(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -2593,9 +2585,9 @@ TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); NotifyKeyArgs args; @@ -2631,10 +2623,10 @@ TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) { mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, - AINPUT_KEYBOARD_TYPE_ALPHABETIC); addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); - addMapperAndConfigure(mapper); + KeyboardInputMapper& mapper = + addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); NotifyKeyArgs args; @@ -2677,8 +2669,8 @@ protected: mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController); } - void testMotionRotation(CursorInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY); + void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY, + int32_t rotatedX, int32_t rotatedY); void prepareDisplay(int32_t orientation) { const std::string uniqueId = "local:0"; @@ -2690,8 +2682,9 @@ protected: const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; -void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) { +void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, + int32_t originalY, int32_t rotatedX, + int32_t rotatedY) { NotifyMotionArgs args; process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX); @@ -2706,28 +2699,25 @@ void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper, } TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "pointer"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); } TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources()); } TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "pointer"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); InputDeviceInfo info; - mapper->populateDeviceInfo(&info); + mapper.populateDeviceInfo(&info); // Initially there may not be a valid motion range. ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); @@ -2739,7 +2729,7 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); InputDeviceInfo info2; - mapper->populateDeviceInfo(&info2); + mapper.populateDeviceInfo(&info2); ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, @@ -2753,12 +2743,11 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF } TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); InputDeviceInfo info; - mapper->populateDeviceInfo(&info); + mapper.populateDeviceInfo(&info); ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL, @@ -2772,9 +2761,8 @@ TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsSca } TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -2865,9 +2853,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat } TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs args; @@ -2889,9 +2876,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { } TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs args; @@ -2923,9 +2909,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { } TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs args; @@ -2971,9 +2956,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { } TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); prepareDisplay(DISPLAY_ORIENTATION_90); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); @@ -2987,10 +2971,9 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMot } TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "navigation"); addConfigurationProperty("cursor.orientationAware", "1"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); prepareDisplay(DISPLAY_ORIENTATION_0); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); @@ -3034,9 +3017,8 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) } TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "pointer"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(100, 200); @@ -3322,9 +3304,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { } TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "pointer"); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(100, 200); @@ -3344,10 +3325,9 @@ TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerArou } TEST_F(CursorInputMapperTest, Process_PointerCapture) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); addConfigurationProperty("cursor.mode", "pointer"); mFakePolicy->setPointerCapture(true); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -3433,8 +3413,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { } TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { - CursorInputMapper* mapper = new CursorInputMapper(mDevice); - addMapperAndConfigure(mapper); + CursorInputMapper& mapper = addMapperAndConfigure(); // Setup for second display. constexpr int32_t SECOND_DISPLAY_ID = 1; @@ -3462,7 +3441,6 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId); } - // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { @@ -3637,15 +3615,15 @@ protected: void prepareButtons(); void prepareAxes(int axes); - void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processUp(SingleTouchInputMapper* mappery); - void processPressure(SingleTouchInputMapper* mapper, int32_t pressure); - void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor); - void processDistance(SingleTouchInputMapper* mapper, int32_t distance); - void processTilt(SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY); - void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value); - void processSync(SingleTouchInputMapper* mapper); + void processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y); + void processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y); + void processUp(SingleTouchInputMapper& mappery); + void processPressure(SingleTouchInputMapper& mapper, int32_t pressure); + void processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor); + void processDistance(SingleTouchInputMapper& mapper, int32_t distance); + void processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY); + void processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value); + void processSync(SingleTouchInputMapper& mapper); }; void SingleTouchInputMapperTest::prepareButtons() { @@ -3679,103 +3657,95 @@ void SingleTouchInputMapperTest::prepareAxes(int axes) { } } -void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { +void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) { process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y); } -void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { +void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y); } -void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) { +void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0); } -void SingleTouchInputMapperTest::processPressure( - SingleTouchInputMapper* mapper, int32_t pressure) { +void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure); } -void SingleTouchInputMapperTest::processToolMajor( - SingleTouchInputMapper* mapper, int32_t toolMajor) { +void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper, + int32_t toolMajor) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor); } -void SingleTouchInputMapperTest::processDistance( - SingleTouchInputMapper* mapper, int32_t distance) { +void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance); } -void SingleTouchInputMapperTest::processTilt( - SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) { +void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, + int32_t tiltY) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY); } -void SingleTouchInputMapperTest::processKey( - SingleTouchInputMapper* mapper, int32_t code, int32_t value) { +void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code, + int32_t value) { process(mapper, ARBITRARY_TIME, EV_KEY, code, value); } -void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { +void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); } - TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchPad"); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); + ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); // Virtual key is down. int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); @@ -3784,27 +3754,26 @@ TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled()); - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); + ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); // Virtual key is up. processUp(mapper); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled()); - ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); + ASSERT_EQ(AKEY_STATE_UP, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); } TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); + ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); // Virtual key is down. int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); @@ -3813,40 +3782,38 @@ TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled()); - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); + ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); // Virtual key is up. processUp(mapper); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled()); - ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); + ASSERT_EQ(AKEY_STATE_UP, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); } TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A }; uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags)); + ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags)); ASSERT_TRUE(flags[0]); ASSERT_FALSE(flags[1]); } TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -3891,13 +3858,12 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor } TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -4013,13 +3979,12 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB } TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -4087,7 +4052,6 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMoves } TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID); @@ -4095,7 +4059,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDispl prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -4186,13 +4150,12 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDispl } TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -4277,12 +4240,11 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { } TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareButtons(); prepareAxes(POSITION); addConfigurationProperty("touch.orientationAware", "0"); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs args; @@ -4301,11 +4263,10 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate } TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs args; @@ -4367,12 +4328,11 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) } TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); // These calculations are based on the input device calibration documentation. int32_t rawX = 100; @@ -4412,13 +4372,12 @@ TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { } TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareLocationCalibration(); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); int32_t rawX = 100; int32_t rawY = 200; @@ -4436,12 +4395,11 @@ TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { } TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; NotifyKeyArgs keyArgs; @@ -4680,12 +4638,11 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) { } TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -4816,13 +4773,12 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { } TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -4889,12 +4845,11 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueI } TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION | PRESSURE); - addMapperAndConfigure(mapper); + SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -4960,27 +4915,26 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } - // --- MultiTouchInputMapperTest --- class MultiTouchInputMapperTest : public TouchInputMapperTest { protected: void prepareAxes(int axes); - void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y); - void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor); - void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor); - void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor); - void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor); - void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation); - void processPressure(MultiTouchInputMapper* mapper, int32_t pressure); - void processDistance(MultiTouchInputMapper* mapper, int32_t distance); - void processId(MultiTouchInputMapper* mapper, int32_t id); - void processSlot(MultiTouchInputMapper* mapper, int32_t slot); - void processToolType(MultiTouchInputMapper* mapper, int32_t toolType); - void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value); - void processMTSync(MultiTouchInputMapper* mapper); - void processSync(MultiTouchInputMapper* mapper); + void processPosition(MultiTouchInputMapper& mapper, int32_t x, int32_t y); + void processTouchMajor(MultiTouchInputMapper& mapper, int32_t touchMajor); + void processTouchMinor(MultiTouchInputMapper& mapper, int32_t touchMinor); + void processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor); + void processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor); + void processOrientation(MultiTouchInputMapper& mapper, int32_t orientation); + void processPressure(MultiTouchInputMapper& mapper, int32_t pressure); + void processDistance(MultiTouchInputMapper& mapper, int32_t distance); + void processId(MultiTouchInputMapper& mapper, int32_t id); + void processSlot(MultiTouchInputMapper& mapper, int32_t slot); + void processToolType(MultiTouchInputMapper& mapper, int32_t toolType); + void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value); + void processMTSync(MultiTouchInputMapper& mapper); + void processSync(MultiTouchInputMapper& mapper); }; void MultiTouchInputMapperTest::prepareAxes(int axes) { @@ -5033,83 +4987,74 @@ void MultiTouchInputMapperTest::prepareAxes(int axes) { } } -void MultiTouchInputMapperTest::processPosition( - MultiTouchInputMapper* mapper, int32_t x, int32_t y) { +void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x, + int32_t y) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y); } -void MultiTouchInputMapperTest::processTouchMajor( - MultiTouchInputMapper* mapper, int32_t touchMajor) { +void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper, + int32_t touchMajor) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor); } -void MultiTouchInputMapperTest::processTouchMinor( - MultiTouchInputMapper* mapper, int32_t touchMinor) { +void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper, + int32_t touchMinor) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor); } -void MultiTouchInputMapperTest::processToolMajor( - MultiTouchInputMapper* mapper, int32_t toolMajor) { +void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor); } -void MultiTouchInputMapperTest::processToolMinor( - MultiTouchInputMapper* mapper, int32_t toolMinor) { +void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor); } -void MultiTouchInputMapperTest::processOrientation( - MultiTouchInputMapper* mapper, int32_t orientation) { +void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper, + int32_t orientation) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation); } -void MultiTouchInputMapperTest::processPressure( - MultiTouchInputMapper* mapper, int32_t pressure) { +void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure); } -void MultiTouchInputMapperTest::processDistance( - MultiTouchInputMapper* mapper, int32_t distance) { +void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance); } -void MultiTouchInputMapperTest::processId( - MultiTouchInputMapper* mapper, int32_t id) { +void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id); } -void MultiTouchInputMapperTest::processSlot( - MultiTouchInputMapper* mapper, int32_t slot) { +void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot); } -void MultiTouchInputMapperTest::processToolType( - MultiTouchInputMapper* mapper, int32_t toolType) { +void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) { process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType); } -void MultiTouchInputMapperTest::processKey( - MultiTouchInputMapper* mapper, int32_t code, int32_t value) { +void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code, + int32_t value) { process(mapper, ARBITRARY_TIME, EV_KEY, code, value); } -void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) { +void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0); } -void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) { +void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); } - TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -5381,12 +5326,11 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackin } TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -5557,12 +5501,11 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId } TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); prepareVirtualKeys(); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); @@ -5728,11 +5671,10 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) { } TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // These calculations are based on the input device calibration documentation. int32_t rawX = 100; @@ -5778,12 +5720,11 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | TOUCH | TOOL | MINOR); addConfigurationProperty("touch.size.calibration", "geometric"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // These calculations are based on the input device calibration documentation. int32_t rawX = 100; @@ -5816,7 +5757,6 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | TOUCH | TOOL); @@ -5824,7 +5764,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibrati addConfigurationProperty("touch.size.scale", "10"); addConfigurationProperty("touch.size.bias", "160"); addConfigurationProperty("touch.size.isSummed", "1"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // These calculations are based on the input device calibration documentation. // Note: We only provide a single common touch/tool value because the device is assumed @@ -5869,14 +5809,13 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibrati } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | TOUCH | TOOL); addConfigurationProperty("touch.size.calibration", "area"); addConfigurationProperty("touch.size.scale", "43"); addConfigurationProperty("touch.size.bias", "3"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // These calculations are based on the input device calibration documentation. int32_t rawX = 100; @@ -5903,16 +5842,15 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) { } TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | PRESSURE); addConfigurationProperty("touch.pressure.calibration", "amplitude"); addConfigurationProperty("touch.pressure.scale", "0.01"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); InputDeviceInfo info; - mapper->populateDeviceInfo(&info); + mapper.populateDeviceInfo(&info); ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN, 0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f)); @@ -5938,11 +5876,10 @@ TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) { } TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; NotifyKeyArgs keyArgs; @@ -6182,11 +6119,10 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) { } TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6333,12 +6269,11 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { } TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6405,11 +6340,10 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs } TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6482,7 +6416,6 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfIts * This can be checked by looking at the displayId of the resulting NotifyMotionArgs. */ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); const std::string usb2 = "USB2"; const uint8_t hdmi1 = 0; const uint8_t hdmi2 = 1; @@ -6491,7 +6424,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); mFakePolicy->addInputPortAssociation(usb2, hdmi2); @@ -6522,14 +6455,13 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { * Expect fallback to internal viewport if device is external and external viewport is not present. */ TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); mDevice->setExternal(true); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); NotifyMotionArgs motionArgs; @@ -6559,13 +6491,12 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // Check source is mouse that would obtain the PointerController. - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); NotifyMotionArgs motionArgs; processPosition(mapper, 100, 100); @@ -6578,10 +6509,9 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Setup the first touch screen device. - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareAxes(POSITION | ID | SLOT); addConfigurationProperty("touch.deviceType", "touchScreen"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; @@ -6606,8 +6536,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { String8("touchScreen")); // Setup the second touch screen device. - MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2.get()); - device2->addMapper(mapper2); + MultiTouchInputMapper& mapper2 = device2->addMapper(); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -6659,11 +6588,10 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { } TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; // Unrotated video frame @@ -6685,10 +6613,9 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { } TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // Unrotated video frame TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); NotifyMotionArgs motionArgs; @@ -6710,10 +6637,9 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { } TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); prepareAxes(POSITION); addConfigurationProperty("touch.deviceType", "touchScreen"); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // Unrotated video frames. There's no rule that they must all have the same dimensions, // so mix these. TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); @@ -6737,7 +6663,6 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { * expected to be disabled, and it should be enabled after the viewport has found. */ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); constexpr uint8_t hdmi2 = 1; const std::string secondaryUniqueId = "uniqueId2"; constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; @@ -6746,7 +6671,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); ASSERT_EQ(mDevice->isEnabled(), false); @@ -6767,11 +6692,10 @@ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { * Test touch should not work if outside of surface. */ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); // Touch on left-top area should work. int32_t rawX = DISPLAY_WIDTH / 2 - 1; @@ -6783,7 +6707,7 @@ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); // Reset. - mapper->reset(ARBITRARY_TIME); + mapper.reset(ARBITRARY_TIME); // Let logical display be different to physical display and rotate 90-degrees. std::optional internalViewport = @@ -6811,11 +6735,10 @@ TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { } TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6857,11 +6780,10 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { * UP events should be ignored. */ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); - addMapperAndConfigure(mapper); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; -- cgit v1.2.3-59-g8ed1b From 26ec22263d16538483cdc963e828f2bcf21a877e Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Fri, 10 Jan 2020 16:30:54 -0800 Subject: Refactor use of services in InputMappers. Create InputDeviceContext class to hide direct EventHub and InputDevice accesses from InputMappers. Test: atest inputflinger_tests libinput_tests Change-Id: I05f9c808fc1a6f4c9207bd29fde50b76ec5655bb --- services/inputflinger/reader/InputDevice.cpp | 41 ++++--- services/inputflinger/reader/InputReader.cpp | 14 +-- services/inputflinger/reader/include/InputDevice.h | 131 ++++++++++++++++++--- services/inputflinger/reader/include/InputReader.h | 6 +- .../reader/include/InputReaderContext.h | 3 +- .../reader/mapper/CursorInputMapper.cpp | 46 ++++---- .../inputflinger/reader/mapper/CursorInputMapper.h | 4 +- .../reader/mapper/ExternalStylusInputMapper.cpp | 12 +- .../reader/mapper/ExternalStylusInputMapper.h | 2 +- .../inputflinger/reader/mapper/InputMapper.cpp | 6 +- services/inputflinger/reader/mapper/InputMapper.h | 18 ++- .../reader/mapper/JoystickInputMapper.cpp | 12 +- .../reader/mapper/JoystickInputMapper.h | 2 +- .../reader/mapper/KeyboardInputMapper.cpp | 38 +++--- .../reader/mapper/KeyboardInputMapper.h | 2 +- .../reader/mapper/MultiTouchInputMapper.cpp | 19 +-- .../reader/mapper/MultiTouchInputMapper.h | 6 +- .../reader/mapper/RotaryEncoderInputMapper.cpp | 24 ++-- .../reader/mapper/RotaryEncoderInputMapper.h | 2 +- .../reader/mapper/SingleTouchInputMapper.cpp | 5 +- .../reader/mapper/SingleTouchInputMapper.h | 2 +- .../reader/mapper/SwitchInputMapper.cpp | 8 +- .../inputflinger/reader/mapper/SwitchInputMapper.h | 2 +- .../reader/mapper/TouchInputMapper.cpp | 104 ++++++++-------- .../inputflinger/reader/mapper/TouchInputMapper.h | 2 +- .../reader/mapper/VibratorInputMapper.cpp | 10 +- .../reader/mapper/VibratorInputMapper.h | 2 +- .../mapper/accumulator/CursorButtonAccumulator.cpp | 18 +-- .../mapper/accumulator/CursorButtonAccumulator.h | 4 +- .../mapper/accumulator/CursorScrollAccumulator.cpp | 8 +- .../mapper/accumulator/CursorScrollAccumulator.h | 6 +- .../accumulator/SingleTouchMotionAccumulator.cpp | 16 +-- .../accumulator/SingleTouchMotionAccumulator.h | 4 +- .../mapper/accumulator/TouchButtonAccumulator.cpp | 42 +++---- .../mapper/accumulator/TouchButtonAccumulator.h | 6 +- services/inputflinger/tests/InputReader_test.cpp | 18 +-- 36 files changed, 377 insertions(+), 268 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 1c08ab1ed2..ae82cd4aa4 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -43,12 +43,14 @@ InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t genera mSources(0), mIsExternal(false), mHasMic(false), - mDropUntilNextSync(false) {} + mDropUntilNextSync(false) { + mDeviceContext = std::make_unique(*this); +} InputDevice::~InputDevice() {} bool InputDevice::isEnabled() { - return getEventHub()->isDeviceEnabled(mId); + return mDeviceContext->isDeviceEnabled(); } void InputDevice::setEnabled(bool enabled, nsecs_t when) { @@ -64,11 +66,11 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { } if (enabled) { - getEventHub()->enableDevice(mId); + mDeviceContext->enableDevice(); reset(when); } else { reset(when); - getEventHub()->disableDevice(mId); + mDeviceContext->disableDevice(); } // Must change generation to flag this device as changed bumpGeneration(); @@ -119,6 +121,7 @@ void InputDevice::dump(std::string& dump) { void InputDevice::populateMappers() { uint32_t classes = mClasses; std::vector>& mappers = mMappers; + std::unique_ptr& contextPtr = mDeviceContext; // External devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { @@ -132,17 +135,17 @@ void InputDevice::populateMappers() { // Switch-like devices. if (classes & INPUT_DEVICE_CLASS_SWITCH) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // Scroll wheel-like devices. if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // Vibrator-like devices. if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // Keyboard-like devices. @@ -163,29 +166,29 @@ void InputDevice::populateMappers() { if (keyboardSource != 0) { mappers.push_back( - std::make_unique(this, keyboardSource, keyboardType)); + std::make_unique(*contextPtr, keyboardSource, keyboardType)); } // Cursor-like devices. if (classes & INPUT_DEVICE_CLASS_CURSOR) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // Touchscreens and touchpad devices. if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // Joystick-like devices. if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } // External stylus-like devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - mappers.push_back(std::make_unique(this)); + mappers.push_back(std::make_unique(*contextPtr)); } } @@ -195,14 +198,14 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config if (!isIgnored()) { if (!changes) { // first time only - mContext->getEventHub()->getConfiguration(mId, &mConfiguration); + mDeviceContext->getConfiguration(&mConfiguration); } if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { sp keyboardLayout = mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { + if (mDeviceContext->setKeyboardLayoutOverlay(keyboardLayout)) { bumpGeneration(); } } @@ -421,4 +424,12 @@ std::optional InputDevice::getAssociatedDisplayId() { [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); }); } +InputDeviceContext::InputDeviceContext(InputDevice& device) + : mDevice(device), + mContext(device.getContext()), + mEventHub(device.getContext()->getEventHub()), + mId(device.getId()) {} + +InputDeviceContext::~InputDeviceContext() {} + } // namespace android diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 010729a6d3..3e23fa6c84 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -348,13 +348,11 @@ void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { mDisableVirtualKeysTimeout = time; } -bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode) { +bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode) { if (now < mDisableVirtualKeysTimeout) { - ALOGI("Dropping virtual key from device %s because virtual keys are " + ALOGI("Dropping virtual key from device because virtual keys are " "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, - scanCode); + (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, scanCode); return true; } else { return false; @@ -662,10 +660,10 @@ void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { mReader->disableVirtualKeysUntilLocked(time); } -bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device, - int32_t keyCode, int32_t scanCode) { +bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, int32_t keyCode, + int32_t scanCode) { // lock is already held by the input loop - return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode); + return mReader->shouldDropVirtualKeyLocked(now, keyCode, scanCode); } void InputReader::ContextImpl::fadePointer() { diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index d06cc20719..0814d1f16e 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -31,6 +31,7 @@ namespace android { +class InputDeviceContext; class InputMapper; /* Represents the state of a single input device. */ @@ -96,30 +97,12 @@ public: inline const PropertyMap& getConfiguration() { return mConfiguration; } inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); } - - bool hasAbsoluteAxis(int32_t code) { - RawAbsoluteAxisInfo info; - getEventHub()->getAbsoluteAxisInfo(mId, code, &info); - return info.valid; - } - - bool isKeyPressed(int32_t code) { - return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; - } - - int32_t getAbsoluteAxisValue(int32_t code) { - int32_t value; - getEventHub()->getAbsoluteAxisValue(mId, code, &value); - return value; - } - std::optional getAssociatedDisplayId(); // construct and add a mapper to the input device template T& addMapper(Args... args) { - T* mapper = new T(this, args...); + T* mapper = new T(*mDeviceContext, args...); mMappers.emplace_back(mapper); return *mapper; } @@ -133,6 +116,7 @@ private: std::string mAlias; uint32_t mClasses; + std::unique_ptr mDeviceContext; std::vector> mMappers; uint32_t mSources; @@ -168,6 +152,115 @@ private: } }; +/* Provides access to EventHub methods, but limits access to the current InputDevice. + * Essentially an implementation of EventHubInterface, but for a specific device id. + * Helps hide implementation details of InputDevice and EventHub. Used by mappers to + * check the status of the associated hardware device + */ +class InputDeviceContext { +public: + InputDeviceContext(InputDevice& device); + ~InputDeviceContext(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() { return mId; } + + inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); } + inline InputDeviceIdentifier getDeviceIdentifier() const { + return mEventHub->getDeviceIdentifier(mId); + } + inline int32_t getDeviceControllerNumber() const { + return mEventHub->getDeviceControllerNumber(mId); + } + inline void getConfiguration(PropertyMap* outConfiguration) const { + return mEventHub->getConfiguration(mId, outConfiguration); + } + inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const { + return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); + } + inline bool hasRelativeAxis(int32_t code) const { + return mEventHub->hasRelativeAxis(mId, code); + } + inline bool hasInputProperty(int property) const { + return mEventHub->hasInputProperty(mId, property); + } + inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { + return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState, + outFlags); + } + inline status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { + return mEventHub->mapAxis(mId, scanCode, outAxisInfo); + } + inline std::vector getVideoFrames() { return mEventHub->getVideoFrames(mId); } + inline int32_t getScanCodeState(int32_t scanCode) const { + return mEventHub->getScanCodeState(mId, scanCode); + } + inline int32_t getKeyCodeState(int32_t keyCode) const { + return mEventHub->getKeyCodeState(mId, keyCode); + } + inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); } + inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const { + return mEventHub->getAbsoluteAxisValue(mId, code, outValue); + } + inline bool markSupportedKeyCodes(size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const { + return mEventHub->markSupportedKeyCodes(mId, numCodes, keyCodes, outFlags); + } + inline bool hasScanCode(int32_t scanCode) const { + return mEventHub->hasScanCode(mId, scanCode); + } + inline bool hasLed(int32_t led) const { return mEventHub->hasLed(mId, led); } + inline void setLedState(int32_t led, bool on) { return mEventHub->setLedState(mId, led, on); } + inline void getVirtualKeyDefinitions(std::vector& outVirtualKeys) const { + return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys); + } + inline sp getKeyCharacterMap() const { + return mEventHub->getKeyCharacterMap(mId); + } + inline bool setKeyboardLayoutOverlay(const sp& map) { + return mEventHub->setKeyboardLayoutOverlay(mId, map); + } + inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); } + inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); } + + inline bool hasAbsoluteAxis(int32_t code) const { + RawAbsoluteAxisInfo info; + mEventHub->getAbsoluteAxisInfo(mId, code, &info); + return info.valid; + } + inline bool isKeyPressed(int32_t code) const { + return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + } + inline int32_t getAbsoluteAxisValue(int32_t code) const { + int32_t value; + mEventHub->getAbsoluteAxisValue(mId, code, &value); + return value; + } + inline bool isDeviceEnabled() { return mEventHub->isDeviceEnabled(mId); } + inline status_t enableDevice() { return mEventHub->enableDevice(mId); } + inline status_t disableDevice() { return mEventHub->disableDevice(mId); } + + inline const std::string getName() { return mDevice.getName(); } + inline const std::string getDescriptor() { return mDevice.getDescriptor(); } + inline bool isExternal() { return mDevice.isExternal(); } + inline std::optional getAssociatedDisplayPort() const { + return mDevice.getAssociatedDisplayPort(); + } + inline std::optional getAssociatedViewport() const { + return mDevice.getAssociatedViewport(); + } + inline void cancelTouch(nsecs_t when) { mDevice.cancelTouch(when); } + inline void bumpGeneration() { mDevice.bumpGeneration(); } + inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); } + +private: + InputDevice& mDevice; + InputReaderContext* mContext; + EventHubInterface* mEventHub; + int32_t mId; +}; + } // namespace android #endif //_UI_INPUTREADER_INPUT_DEVICE_H diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 02957cd6ee..cf1af04417 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -101,8 +101,7 @@ protected: virtual void updateGlobalMetaState() override; virtual int32_t getGlobalMetaState() override; virtual void disableVirtualKeysUntil(nsecs_t time) override; - virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode) override; + virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; virtual void fadePointer() override; virtual void requestTimeoutAtTime(nsecs_t when) override; virtual int32_t bumpGeneration() override; @@ -168,8 +167,7 @@ private: nsecs_t mDisableVirtualKeysTimeout; void disableVirtualKeysUntilLocked(nsecs_t time); - bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode); + bool shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode); nsecs_t mNextTimeout; void requestTimeoutAtTimeLocked(nsecs_t when); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 3472346d44..e14fbbec3d 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -42,8 +42,7 @@ public: virtual int32_t getGlobalMetaState() = 0; virtual void disableVirtualKeysUntil(nsecs_t time) = 0; - virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode) = 0; + virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0; virtual void fadePointer() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 69a75bae2f..78f33823d0 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -30,7 +30,7 @@ CursorMotionAccumulator::CursorMotionAccumulator() { clearRelativeAxes(); } -void CursorMotionAccumulator::reset(InputDevice* device) { +void CursorMotionAccumulator::reset(InputDeviceContext& deviceContext) { clearRelativeAxes(); } @@ -58,7 +58,8 @@ void CursorMotionAccumulator::finishSync() { // --- CursorInputMapper --- -CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} +CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} CursorInputMapper::~CursorInputMapper() {} @@ -113,7 +114,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* InputMapper::configure(when, config, changes); if (!changes) { // first time only - mCursorScrollAccumulator.configure(getDevice()); + mCursorScrollAccumulator.configure(getDeviceContext()); // Configure basic parameters. configureParameters(); @@ -167,7 +168,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } bumpGeneration(); if (changes) { - getDevice()->notifyReset(when); + NotifyDeviceResetArgs args(getContext()->getNextSequenceNum(), when, getDeviceId()); + getListener()->notifyDeviceReset(&args); } } @@ -218,7 +220,8 @@ void CursorInputMapper::updatePointerControllerDisplayViewport( void CursorInputMapper::configureParameters() { mParameters.mode = Parameters::MODE_POINTER; String8 cursorModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { + if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"), + cursorModeString)) { if (cursorModeString == "navigation") { mParameters.mode = Parameters::MODE_NAVIGATION; } else if (cursorModeString != "pointer" && cursorModeString != "default") { @@ -227,8 +230,8 @@ void CursorInputMapper::configureParameters() { } mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), - mParameters.orientationAware); + getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { @@ -266,9 +269,9 @@ void CursorInputMapper::reset(nsecs_t when) { mWheelXVelocityControl.reset(); mWheelYVelocityControl.reset(); - mCursorButtonAccumulator.reset(getDevice()); - mCursorMotionAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); + mCursorButtonAccumulator.reset(getDeviceContext()); + mCursorMotionAccumulator.reset(getDeviceContext()); + mCursorScrollAccumulator.reset(getDeviceContext()); InputMapper::reset(when); } @@ -369,7 +372,7 @@ void CursorInputMapper::sync(nsecs_t when) { // the device in your pocket. // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { + if ((buttonsPressed || moved || scrolled) && getDeviceContext().isExternal()) { policyFlags |= POLICY_FLAG_WAKE; } @@ -379,7 +382,7 @@ void CursorInputMapper::sync(nsecs_t when) { // Send motion event. if (downChanged || moved || scrolled || buttonsChanged) { - int32_t metaState = mContext->getGlobalMetaState(); + int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = lastButtonState; int32_t motionEventAction; if (downChanged) { @@ -395,8 +398,8 @@ void CursorInputMapper::sync(nsecs_t when) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, + NotifyMotionArgs releaseArgs(getContext()->getNextSequenceNum(), when, + getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -407,7 +410,7 @@ void CursorInputMapper::sync(nsecs_t when) { } } - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, @@ -420,7 +423,7 @@ void CursorInputMapper::sync(nsecs_t when) { while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + NotifyMotionArgs pressArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, @@ -436,9 +439,10 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - 0, metaState, currentButtonState, MotionClassification::NONE, + NotifyMotionArgs hoverArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); @@ -450,7 +454,7 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + NotifyMotionArgs scrollArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, MotionClassification::NONE, @@ -471,7 +475,7 @@ void CursorInputMapper::sync(nsecs_t when) { int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + return getDeviceContext().getScanCodeState(scanCode); } else { return AKEY_STATE_UNKNOWN; } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index d56f9be045..94ab30674d 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -36,7 +36,7 @@ class CursorScrollAccumulator; class CursorMotionAccumulator { public: CursorMotionAccumulator(); - void reset(InputDevice* device); + void reset(InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); void finishSync(); @@ -53,7 +53,7 @@ private: class CursorInputMapper : public InputMapper { public: - explicit CursorInputMapper(InputDevice* device); + explicit CursorInputMapper(InputDeviceContext& deviceContext); virtual ~CursorInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 9aa0770245..37e4047890 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -23,7 +23,8 @@ namespace android { -ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} +ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} uint32_t ExternalStylusInputMapper::getSources() { return AINPUT_SOURCE_STYLUS; @@ -46,13 +47,12 @@ void ExternalStylusInputMapper::dump(std::string& dump) { void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDevice()); + mTouchButtonAccumulator.configure(getDeviceContext()); } void ExternalStylusInputMapper::reset(nsecs_t when) { - InputDevice* device = getDevice(); - mSingleTouchMotionAccumulator.reset(device); - mTouchButtonAccumulator.reset(device); + mSingleTouchMotionAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(getDeviceContext()); InputMapper::reset(when); } @@ -86,7 +86,7 @@ void ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - mContext->dispatchExternalStylusState(mStylusState); + getContext()->dispatchExternalStylusState(mStylusState); } } // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 34f339a3cd..1d42b30fe2 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -27,7 +27,7 @@ namespace android { class ExternalStylusInputMapper : public InputMapper { public: - explicit ExternalStylusInputMapper(InputDevice* device); + explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext); virtual ~ExternalStylusInputMapper() = default; virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index d941528d14..92af6123cc 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -22,7 +22,7 @@ namespace android { -InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} +InputMapper::InputMapper(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {} InputMapper::~InputMapper() {} @@ -74,11 +74,11 @@ void InputMapper::updateExternalStylusState(const StylusState& state) {} void InputMapper::fadePointer() {} status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); + return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo); } void InputMapper::bumpGeneration() { - mDevice->bumpGeneration(); + getDeviceContext().bumpGeneration(); } void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index a559ef882f..09888bfe20 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -39,16 +39,15 @@ namespace android { */ class InputMapper { public: - explicit InputMapper(InputDevice* device); + explicit InputMapper(InputDeviceContext& deviceContext); virtual ~InputMapper(); - inline InputDevice* getDevice() { return mDevice; } - inline int32_t getDeviceId() { return mDevice->getId(); } - inline const std::string getDeviceName() { return mDevice->getName(); } - inline InputReaderContext* getContext() { return mContext; } - inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline InputListenerInterface* getListener() { return mContext->getListener(); } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + inline int32_t getDeviceId() { return mDeviceContext.getId(); } + inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline const std::string getDeviceName() { return mDeviceContext.getName(); } + inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } + inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } + inline InputListenerInterface* getListener() { return getContext()->getListener(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); @@ -76,8 +75,7 @@ public: virtual std::optional getAssociatedDisplayId() { return std::nullopt; } protected: - InputDevice* mDevice; - InputReaderContext* mContext; + InputDeviceContext& mDeviceContext; status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); void bumpGeneration(); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 50adf73c76..57c85b6280 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -20,7 +20,8 @@ namespace android { -JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} +JoystickInputMapper::JoystickInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} JoystickInputMapper::~JoystickInputMapper() {} @@ -112,7 +113,8 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration if (!changes) { // first time only // Collect all axes. for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { + if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) & + INPUT_DEVICE_CLASS_JOYSTICK)) { continue; // axis must be claimed by a different device } @@ -121,7 +123,7 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration if (rawAxisInfo.valid) { // Map axis. AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo); if (!explicitlyMapped) { // Axis is not explicitly mapped, will choose a generic axis later. axisInfo.mode = AxisInfo::MODE_NORMAL; @@ -304,7 +306,7 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { return; } - int32_t metaState = mContext->getGlobalMetaState(); + int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = 0; PointerProperties pointerProperties; @@ -331,7 +333,7 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index b46d27d2d1..823a096d68 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -23,7 +23,7 @@ namespace android { class JoystickInputMapper : public InputMapper { public: - explicit JoystickInputMapper(InputDevice* device); + explicit JoystickInputMapper(InputDeviceContext& deviceContext); virtual ~JoystickInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 348a7ada0e..9ab707fb60 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -87,8 +87,9 @@ static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { // --- KeyboardInputMapper --- -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) - : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} +KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, + int32_t keyboardType) + : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {} KeyboardInputMapper::~KeyboardInputMapper() {} @@ -114,7 +115,7 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); + info->setKeyCharacterMap(getDeviceContext().getKeyCharacterMap()); } void KeyboardInputMapper::dump(std::string& dump) { @@ -129,10 +130,10 @@ void KeyboardInputMapper::dump(std::string& dump) { std::optional KeyboardInputMapper::findViewport( nsecs_t when, const InputReaderConfiguration* config) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + const std::optional displayPort = getDeviceContext().getAssociatedDisplayPort(); if (displayPort) { // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); + return getDeviceContext().getAssociatedViewport(); } // No associated display defined, try to find default display if orientationAware. @@ -171,7 +172,7 @@ static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* p void KeyboardInputMapper::configureParameters() { mParameters.orientationAware = false; - const PropertyMap& config = getDevice()->getConfiguration(); + const PropertyMap& config = getDeviceContext().getConfiguration(); config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); if (mParameters.orientationAware) { @@ -271,8 +272,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t keyMetaState; uint32_t policyFlags; - if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, - &keyMetaState, &policyFlags)) { + if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState, + &policyFlags)) { keyCode = AKEYCODE_UNKNOWN; keyMetaState = mMetaState; policyFlags = 0; @@ -292,11 +293,11 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, } else { // key down if ((policyFlags & POLICY_FLAG_VIRTUAL) && - mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { + getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) { return; } if (policyFlags & POLICY_FLAG_GESTURE) { - mDevice->cancelTouch(when); + getDeviceContext().cancelTouch(when); } KeyDown keyDown; @@ -338,7 +339,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each // wake key individually. // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault && + if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault && !isMediaKey(keyCode)) { policyFlags |= POLICY_FLAG_WAKE; } @@ -347,8 +348,9 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), - policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + NotifyKeyArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, + getDisplayId(), policyFlags, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); getListener()->notifyKey(&args); } @@ -364,16 +366,16 @@ ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { } int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); + return getDeviceContext().getKeyCodeState(keyCode); } int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + return getDeviceContext().getScanCodeState(scanCode); } bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); + return getDeviceContext().markSupportedKeyCodes(numCodes, keyCodes, outFlags); } int32_t KeyboardInputMapper::getMetaState() { @@ -407,7 +409,7 @@ void KeyboardInputMapper::resetLedState() { } void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { - ledState.avail = getEventHub()->hasLed(getDeviceId(), led); + ledState.avail = getDeviceContext().hasLed(led); ledState.on = false; } @@ -422,7 +424,7 @@ void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t if (ledState.avail) { bool desiredState = (mMetaState & modifier) != 0; if (reset || ledState.on != desiredState) { - getEventHub()->setLedState(getDeviceId(), led, desiredState); + getDeviceContext().setLedState(led, desiredState); ledState.on = desiredState; } } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index badbcb26cf..0bdeded285 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -23,7 +23,7 @@ namespace android { class KeyboardInputMapper : public InputMapper { public: - KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); + KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType); virtual ~KeyboardInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index f42ddcf461..d195a07eff 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -38,17 +38,17 @@ MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { delete[] mSlots; } -void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, +void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol) { mSlotCount = slotCount; mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); delete[] mSlots; mSlots = new Slot[slotCount]; } -void MultiTouchMotionAccumulator::reset(InputDevice* device) { +void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) { // Unfortunately there is no way to read the initial contents of the slots. // So when we reset the accumulator, we must assume they are all zeroes. if (mUsingSlotsProtocol) { @@ -62,8 +62,7 @@ void MultiTouchMotionAccumulator::reset(InputDevice* device) { // This can cause the touch point to "jump", but at least there will be // no stuck touches. int32_t initialSlot; - status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, - &initialSlot); + status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); if (status) { ALOGD("Could not retrieve current multitouch slot index. status=%d", status); initialSlot = -1; @@ -218,12 +217,13 @@ int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { // --- MultiTouchInputMapper --- -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} +MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext) + : TouchInputMapper(deviceContext) {} MultiTouchInputMapper::~MultiTouchInputMapper() {} void MultiTouchInputMapper::reset(nsecs_t when) { - mMultiTouchMotionAccumulator.reset(getDevice()); + mMultiTouchMotionAccumulator.reset(getDeviceContext()); mPointerIdBits.clear(); @@ -353,9 +353,10 @@ void MultiTouchInputMapper::configureRawPointerAxes() { getDeviceName().c_str(), slotCount, MAX_SLOTS); slotCount = MAX_SLOTS; } - mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); + mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount, + true /*usingSlotsProtocol*/); } else { - mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, + mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS, false /*usingSlotsProtocol*/); } } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index a45c3cb12b..89ef41d177 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -70,8 +70,8 @@ public: MultiTouchMotionAccumulator(); ~MultiTouchMotionAccumulator(); - void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); - void reset(InputDevice* device); + void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); + void reset(InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); void finishSync(); bool hasStylus() const; @@ -91,7 +91,7 @@ private: class MultiTouchInputMapper : public TouchInputMapper { public: - explicit MultiTouchInputMapper(InputDevice* device); + explicit MultiTouchInputMapper(InputDeviceContext& deviceContext); virtual ~MultiTouchInputMapper(); virtual void reset(nsecs_t when) override; diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index e113ccad84..a1cce5695b 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -22,8 +22,8 @@ namespace android { -RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) - : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) { mSource = AINPUT_SOURCE_ROTARY_ENCODER; } @@ -38,11 +38,11 @@ void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { float res = 0.0f; - if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { + if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) { ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); } - if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), - mScalingFactor)) { + if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"), + mScalingFactor)) { ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," "default to 1.0!\n"); mScalingFactor = 1.0f; @@ -62,7 +62,7 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfigur uint32_t changes) { InputMapper::configure(when, config, changes); if (!changes) { - mRotaryEncoderScrollAccumulator.configure(getDevice()); + mRotaryEncoderScrollAccumulator.configure(getDeviceContext()); } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { std::optional internalViewport = @@ -76,7 +76,7 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfigur } void RotaryEncoderInputMapper::reset(nsecs_t when) { - mRotaryEncoderScrollAccumulator.reset(getDevice()); + mRotaryEncoderScrollAccumulator.reset(getDeviceContext()); InputMapper::reset(when); } @@ -106,7 +106,7 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { // Moving the rotary encoder should wake the device (if specified). uint32_t policyFlags = 0; - if (scrolled && getDevice()->isExternal()) { + if (scrolled && getDeviceContext().isExternal()) { policyFlags |= POLICY_FLAG_WAKE; } @@ -116,12 +116,12 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { // Send motion event. if (scrolled) { - int32_t metaState = mContext->getGlobalMetaState(); + int32_t metaState = getContext()->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, /* buttonState */ 0, MotionClassification::NONE, + NotifyMotionArgs scrollArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, + 0, metaState, /* buttonState */ 0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 38c7258f7e..7a77b1286c 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -24,7 +24,7 @@ namespace android { class RotaryEncoderInputMapper : public InputMapper { public: - explicit RotaryEncoderInputMapper(InputDevice* device); + explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext); virtual ~RotaryEncoderInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp index 440d282686..4fff9bebb5 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -18,12 +18,13 @@ namespace android { -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} +SingleTouchInputMapper::SingleTouchInputMapper(InputDeviceContext& deviceContext) + : TouchInputMapper(deviceContext) {} SingleTouchInputMapper::~SingleTouchInputMapper() {} void SingleTouchInputMapper::reset(nsecs_t when) { - mSingleTouchMotionAccumulator.reset(getDevice()); + mSingleTouchMotionAccumulator.reset(getDeviceContext()); TouchInputMapper::reset(when); } diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h index 8438eee80b..f5befb37de 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -24,7 +24,7 @@ namespace android { class SingleTouchInputMapper : public TouchInputMapper { public: - explicit SingleTouchInputMapper(InputDevice* device); + explicit SingleTouchInputMapper(InputDeviceContext& deviceContext); virtual ~SingleTouchInputMapper(); virtual void reset(nsecs_t when) override; diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 16095b9eb1..52b24498ac 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -20,8 +20,8 @@ namespace android { -SwitchInputMapper::SwitchInputMapper(InputDevice* device) - : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} +SwitchInputMapper::SwitchInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext), mSwitchValues(0), mUpdatedSwitchMask(0) {} SwitchInputMapper::~SwitchInputMapper() {} @@ -56,7 +56,7 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { void SwitchInputMapper::sync(nsecs_t when) { if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0 /*policyFlags*/, + NotifySwitchArgs args(getContext()->getNextSequenceNum(), when, 0 /*policyFlags*/, updatedSwitchValues, mUpdatedSwitchMask); getListener()->notifySwitch(&args); @@ -65,7 +65,7 @@ void SwitchInputMapper::sync(nsecs_t when) { } int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); + return getDeviceContext().getSwitchState(switchCode); } void SwitchInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index e65d4e2bd5..4d74163069 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -23,7 +23,7 @@ namespace android { class SwitchInputMapper : public InputMapper { public: - explicit SwitchInputMapper(InputDevice* device); + explicit SwitchInputMapper(InputDeviceContext& deviceContext); virtual ~SwitchInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3b20173fa4..e832804e16 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -156,8 +156,8 @@ void CookedPointerData::copyFrom(const CookedPointerData& other) { // --- TouchInputMapper --- -TouchInputMapper::TouchInputMapper(InputDevice* device) - : InputMapper(device), +TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext), mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), mSurfaceWidth(-1), @@ -348,8 +348,8 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c configureParameters(); // Configure common accumulators. - mCursorScrollAccumulator.configure(getDevice()); - mTouchButtonAccumulator.configure(getDevice()); + mCursorScrollAccumulator.configure(getDeviceContext()); + mTouchButtonAccumulator.configure(getDeviceContext()); // Configure absolute axis information. configureRawPointerAxes(); @@ -386,13 +386,14 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c if (changes && resetNeeded) { // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - getDevice()->notifyReset(when); + NotifyDeviceResetArgs args(getContext()->getNextSequenceNum(), when, getDeviceId()); + getListener()->notifyDeviceReset(&args); } } void TouchInputMapper::resolveExternalStylusPresence() { std::vector devices; - mContext->getExternalStylusDevices(devices); + getContext()->getExternalStylusDevices(devices); mExternalStylusConnected = !devices.empty(); if (!mExternalStylusConnected) { @@ -404,13 +405,13 @@ void TouchInputMapper::configureParameters() { // Use the pointer presentation mode for devices that do not support distinct // multitouch. The spot-based presentation relies on being able to accurately // locate two or more fingers on the touch pad. - mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) + mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT) ? Parameters::GESTURE_MODE_SINGLE_TOUCH : Parameters::GESTURE_MODE_MULTI_TOUCH; String8 gestureModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), - gestureModeString)) { + if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"), + gestureModeString)) { if (gestureModeString == "single-touch") { mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; } else if (gestureModeString == "multi-touch") { @@ -420,14 +421,14 @@ void TouchInputMapper::configureParameters() { } } - if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) { // The device is a touch screen. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) { // The device is a pointing device like a track pad. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || - getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + } else if (getDeviceContext().hasRelativeAxis(REL_X) || + getDeviceContext().hasRelativeAxis(REL_Y)) { // The device is a cursor device with a touch pad attached. // By default don't use the touch pad to move the pointer. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; @@ -436,12 +437,11 @@ void TouchInputMapper::configureParameters() { mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } - mParameters.hasButtonUnderPad = - getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); + mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD); String8 deviceTypeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), - deviceTypeString)) { + if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { @@ -456,8 +456,8 @@ void TouchInputMapper::configureParameters() { } mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; - getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), - mParameters.orientationAware); + getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; mParameters.associatedDisplayIsExternal = false; @@ -466,22 +466,22 @@ void TouchInputMapper::configureParameters() { mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { mParameters.hasAssociatedDisplay = true; if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal(); String8 uniqueDisplayId; - getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - uniqueDisplayId); + getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"), + uniqueDisplayId); mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); } } - if (getDevice()->getAssociatedDisplayPort()) { + if (getDeviceContext().getAssociatedDisplayPort()) { mParameters.hasAssociatedDisplay = true; } // Initial downs on external touch devices should wake the device. // Normally we don't do this for internal touch screens to prevent them from waking // up in your pocket but you can enable it using the input device configuration. - mParameters.wake = getDevice()->isExternal(); - getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); + mParameters.wake = getDeviceContext().isExternal(); + getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); } void TouchInputMapper::dumpParameters(std::string& dump) { @@ -559,10 +559,10 @@ bool TouchInputMapper::hasExternalStylus() const { */ std::optional TouchInputMapper::findViewport() { if (mParameters.hasAssociatedDisplay) { - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + const std::optional displayPort = getDeviceContext().getAssociatedDisplayPort(); if (displayPort) { // Find the viewport that contains the same port - return mDevice->getAssociatedViewport(); + return getDeviceContext().getAssociatedViewport(); } if (mDeviceMode == DEVICE_MODE_POINTER) { @@ -1045,7 +1045,7 @@ void TouchInputMapper::dumpSurface(std::string& dump) { void TouchInputMapper::configureVirtualKeys() { std::vector virtualKeyDefinitions; - getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); + getDeviceContext().getVirtualKeyDefinitions(virtualKeyDefinitions); mVirtualKeys.clear(); @@ -1065,8 +1065,8 @@ void TouchInputMapper::configureVirtualKeys() { int32_t keyCode; int32_t dummyKeyMetaState; uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, - &dummyKeyMetaState, &flags)) { + if (getDeviceContext().mapKey(virtualKey.scanCode, 0, 0, &keyCode, &dummyKeyMetaState, + &flags)) { ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); continue; // drop the key } @@ -1109,7 +1109,7 @@ void TouchInputMapper::dumpVirtualKeys(std::string& dump) { } void TouchInputMapper::parseCalibration() { - const PropertyMap& in = getDevice()->getConfiguration(); + const PropertyMap& in = getDeviceContext().getConfiguration(); Calibration& out = mCalibration; // Size @@ -1353,14 +1353,14 @@ void TouchInputMapper::dumpAffineTransformation(std::string& dump) { } void TouchInputMapper::updateAffineTransformation() { - mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), + mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(), mSurfaceOrientation); } void TouchInputMapper::reset(nsecs_t when) { - mCursorButtonAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - mTouchButtonAccumulator.reset(getDevice()); + mCursorButtonAccumulator.reset(getDeviceContext()); + mCursorScrollAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(getDeviceContext()); mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); @@ -1782,8 +1782,8 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { mCurrentVirtualKey.keyCode = virtualKey->keyCode; mCurrentVirtualKey.scanCode = virtualKey->scanCode; mCurrentVirtualKey.ignored = - mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, - virtualKey->scanCode); + getContext()->shouldDropVirtualKey(when, virtualKey->keyCode, + virtualKey->scanCode); if (!mCurrentVirtualKey.ignored) { #if DEBUG_VIRTUAL_KEYS @@ -1816,7 +1816,7 @@ bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { // is displayed. if (mConfig.virtualKeyQuietTime > 0 && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); + getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); } return false; } @@ -1826,12 +1826,12 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyCode = mCurrentVirtualKey.keyCode; int32_t scanCode = mCurrentVirtualKey.scanCode; nsecs_t downTime = mCurrentVirtualKey.downTime; - int32_t metaState = mContext->getGlobalMetaState(); + int32_t metaState = getContext()->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, - scanCode, metaState, downTime); + NotifyKeyArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, + keyEventFlags, keyCode, scanCode, metaState, downTime); getListener()->notifyKey(&args); } @@ -2503,7 +2503,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, @@ -3423,7 +3423,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, @@ -3437,7 +3437,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, @@ -3453,7 +3453,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, @@ -3464,7 +3464,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, @@ -3479,7 +3479,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, @@ -3490,7 +3490,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, @@ -3512,7 +3512,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, @@ -3583,10 +3583,10 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 } const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); - std::vector frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::vector frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 4b1c0cbeaf..3a61206481 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -132,7 +132,7 @@ struct CookedPointerData { class TouchInputMapper : public InputMapper { public: - explicit TouchInputMapper(InputDevice* device); + explicit TouchInputMapper(InputDeviceContext& deviceContext); virtual ~TouchInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index a27fab4581..1b584ea7b9 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -20,8 +20,8 @@ namespace android { -VibratorInputMapper::VibratorInputMapper(InputDevice* device) - : InputMapper(device), mVibrating(false) {} +VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext), mVibrating(false) {} VibratorInputMapper::~VibratorInputMapper() {} @@ -100,12 +100,12 @@ void VibratorInputMapper::nextStep() { #if DEBUG_VIBRATOR ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); #endif - getEventHub()->vibrate(getDeviceId(), duration); + getDeviceContext().vibrate(duration); } else { #if DEBUG_VIBRATOR ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); #endif - getEventHub()->cancelVibrate(getDeviceId()); + getDeviceContext().cancelVibrate(); } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); mNextStepTime = now + duration; @@ -120,7 +120,7 @@ void VibratorInputMapper::stopVibrating() { #if DEBUG_VIBRATOR ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); #endif - getEventHub()->cancelVibrate(getDeviceId()); + getDeviceContext().cancelVibrate(); } void VibratorInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index dc67890a31..f69fdde22e 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -23,7 +23,7 @@ namespace android { class VibratorInputMapper : public InputMapper { public: - explicit VibratorInputMapper(InputDevice* device); + explicit VibratorInputMapper(InputDeviceContext& deviceContext); virtual ~VibratorInputMapper(); virtual uint32_t getSources() override; diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp index 0337d51126..2d7d73b4a3 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp @@ -25,15 +25,15 @@ CursorButtonAccumulator::CursorButtonAccumulator() { clearButtons(); } -void CursorButtonAccumulator::reset(InputDevice* device) { - mBtnLeft = device->isKeyPressed(BTN_LEFT); - mBtnRight = device->isKeyPressed(BTN_RIGHT); - mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); - mBtnBack = device->isKeyPressed(BTN_BACK); - mBtnSide = device->isKeyPressed(BTN_SIDE); - mBtnForward = device->isKeyPressed(BTN_FORWARD); - mBtnExtra = device->isKeyPressed(BTN_EXTRA); - mBtnTask = device->isKeyPressed(BTN_TASK); +void CursorButtonAccumulator::reset(InputDeviceContext& deviceContext) { + mBtnLeft = deviceContext.isKeyPressed(BTN_LEFT); + mBtnRight = deviceContext.isKeyPressed(BTN_RIGHT); + mBtnMiddle = deviceContext.isKeyPressed(BTN_MIDDLE); + mBtnBack = deviceContext.isKeyPressed(BTN_BACK); + mBtnSide = deviceContext.isKeyPressed(BTN_SIDE); + mBtnForward = deviceContext.isKeyPressed(BTN_FORWARD); + mBtnExtra = deviceContext.isKeyPressed(BTN_EXTRA); + mBtnTask = deviceContext.isKeyPressed(BTN_TASK); } void CursorButtonAccumulator::clearButtons() { diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h index d9123109a3..9e159064fa 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h @@ -21,14 +21,14 @@ namespace android { -class InputDevice; +class InputDeviceContext; struct RawEvent; /* Keeps track of the state of mouse or touch pad buttons. */ class CursorButtonAccumulator { public: CursorButtonAccumulator(); - void reset(InputDevice* device); + void reset(InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp index d744096d94..07146941fd 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp @@ -25,12 +25,12 @@ CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHave clearRelativeAxes(); } -void CursorScrollAccumulator::configure(InputDevice* device) { - mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); - mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +void CursorScrollAccumulator::configure(InputDeviceContext& deviceContext) { + mHaveRelWheel = deviceContext.hasRelativeAxis(REL_WHEEL); + mHaveRelHWheel = deviceContext.hasRelativeAxis(REL_HWHEEL); } -void CursorScrollAccumulator::reset(InputDevice* device) { +void CursorScrollAccumulator::reset(InputDeviceContext& deviceContext) { clearRelativeAxes(); } diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h index 85f331fd8a..16495599d7 100644 --- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -21,7 +21,7 @@ namespace android { -class InputDevice; +class InputDeviceContext; struct RawEvent; /* Keeps track of cursor scrolling motions. */ @@ -29,8 +29,8 @@ struct RawEvent; class CursorScrollAccumulator { public: CursorScrollAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); + void configure(InputDeviceContext& deviceContext); + void reset(InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); void finishSync(); diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp index e9ba727a0d..27b8e40fc6 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp @@ -25,14 +25,14 @@ SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { clearAbsoluteAxes(); } -void SingleTouchMotionAccumulator::reset(InputDevice* device) { - mAbsX = device->getAbsoluteAxisValue(ABS_X); - mAbsY = device->getAbsoluteAxisValue(ABS_Y); - mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); +void SingleTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) { + mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X); + mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y); + mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE); + mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH); + mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE); + mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X); + mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y); } void SingleTouchMotionAccumulator::clearAbsoluteAxes() { diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h index 75f8a961b3..4c011f16a7 100644 --- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h @@ -21,7 +21,7 @@ namespace android { -class InputDevice; +class InputDeviceContext; struct RawEvent; /* Keeps track of the state of single-touch protocol. */ @@ -30,7 +30,7 @@ public: SingleTouchMotionAccumulator(); void process(const RawEvent* rawEvent); - void reset(InputDevice* device); + void reset(InputDeviceContext& deviceContext); inline int32_t getAbsoluteX() const { return mAbsX; } inline int32_t getAbsoluteY() const { return mAbsY; } diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index d2f06c86fd..86153d3f5e 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -25,29 +25,31 @@ TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveSt clearButtons(); } -void TouchButtonAccumulator::configure(InputDevice* device) { - mHaveBtnTouch = device->hasKey(BTN_TOUCH); - mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || - device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || - device->hasKey(BTN_TOOL_AIRBRUSH); +void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) { + mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH); + mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) || + deviceContext.hasScanCode(BTN_TOOL_RUBBER) || + deviceContext.hasScanCode(BTN_TOOL_BRUSH) || + deviceContext.hasScanCode(BTN_TOOL_PENCIL) || + deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH); } -void TouchButtonAccumulator::reset(InputDevice* device) { - mBtnTouch = device->isKeyPressed(BTN_TOUCH); - mBtnStylus = device->isKeyPressed(BTN_STYLUS); +void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) { + mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH); + mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS); // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); - mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); + mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0); + mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP); } void TouchButtonAccumulator::clearButtons() { diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index 65b6bdcc12..22ebb720d5 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -21,15 +21,15 @@ namespace android { -class InputDevice; +class InputDeviceContext; struct RawEvent; /* Keeps track of the state of touch, stylus and tool buttons. */ class TouchButtonAccumulator { public: TouchButtonAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); + void configure(InputDeviceContext& deviceContext); + void reset(InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 7cd8793d5a..01bd9db76f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -881,9 +881,7 @@ private: virtual void disableVirtualKeysUntil(nsecs_t) { } - virtual bool shouldDropVirtualKey(nsecs_t, InputDevice*, int32_t, int32_t) { - return false; - } + virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; } virtual void fadePointer() { } @@ -929,12 +927,14 @@ class FakeInputMapper : public InputMapper { std::optional mViewport; public: - FakeInputMapper(InputDevice* device, uint32_t sources) : - InputMapper(device), - mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE), + FakeInputMapper(InputDeviceContext& deviceContext, uint32_t sources) + : InputMapper(deviceContext), + mSources(sources), + mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE), mMetaState(0), - mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) { - } + mConfigureWasCalled(false), + mResetWasCalled(false), + mProcessWasCalled(false) {} virtual ~FakeInputMapper() { } @@ -1022,7 +1022,7 @@ private: mConfigureWasCalled = true; // Find the associated viewport if exist. - const std::optional displayPort = mDevice->getAssociatedDisplayPort(); + const std::optional displayPort = getDeviceContext().getAssociatedDisplayPort(); if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mViewport = config->getDisplayViewportByPort(*displayPort); } -- cgit v1.2.3-59-g8ed1b From e908789636ec55c04d9e00c406520b5cab19007a Mon Sep 17 00:00:00 2001 From: Gang Wang Date: Tue, 7 Jan 2020 12:17:14 -0500 Subject: Use hmac to sign events in InputDispatcher InputDispatcher will now be able to sign events. Add two static methods to InputDispatcher class, for signing events, and for checking the event signature. The new methods can verify whether a MotionEntry object was actually created by the Input system and whether the MotionEntry object was modified afterwards. Bug: 134977432 Test: none Change-Id: Ica11bccd0c3fb065b02634bd236ae5a46f6b19ee --- services/inputflinger/benchmarks/Android.bp | 1 + services/inputflinger/dispatcher/Entry.cpp | 26 +++++++++++++ services/inputflinger/dispatcher/Entry.h | 3 ++ .../inputflinger/dispatcher/InputDispatcher.cpp | 45 ++++++++++++++++++++-- services/inputflinger/dispatcher/InputDispatcher.h | 2 + .../inputflinger/tests/InputDispatcher_test.cpp | 38 ++++++++++++++++++ 6 files changed, 112 insertions(+), 3 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index 385b9816bd..066a816069 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -7,6 +7,7 @@ cc_benchmark { shared_libs: [ "libbase", "libbinder", + "libcrypto", "libcutils", "libinput", "libinputflinger_base", diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index b7236547de..c4b3789090 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -57,6 +57,32 @@ static std::string keyActionToString(int32_t action) { } return StringPrintf("%" PRId32, action); } +VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { + return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, + entry.displayId}, + entry.action, + entry.downTime, + entry.flags & VERIFIED_KEY_EVENT_FLAGS, + entry.keyCode, + entry.scanCode, + entry.metaState, + entry.repeatCount}; +} + +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) { + const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); + const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK; + return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source, + entry.displayId}, + rawX, + rawY, + actionMasked, + entry.downTime, + entry.flags & VERIFIED_MOTION_EVENT_FLAGS, + entry.metaState, + entry.buttonState}; +} // --- EventEntry --- diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index e8c37f0e6e..b5b61ccb98 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -220,6 +220,9 @@ private: static uint32_t nextSeq(); }; +VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry); +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry); + class InputDispatcher; // A command entry captures state and behavior for an action to be performed in the // dispatch loop after the initial processing has taken place. It is essentially diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 75bc0aa7c8..f9a86dd6bb 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2433,12 +2433,16 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, switch (eventEntry->type) { case EventEntry::Type::KEY: { KeyEntry* keyEntry = static_cast(eventEntry); + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(*keyEntry); + verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_KEY_EVENT_FLAGS; + verifiedEvent.action = dispatchEntry->resolvedAction; + std::array hmac = mHmacKeyManager.sign(verifiedEvent); // Publish the key event. status = connection->inputPublisher .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - INVALID_HMAC, dispatchEntry->resolvedAction, + std::move(hmac), dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, @@ -2482,12 +2486,18 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, usingCoords = scaledCoords; } } + VerifiedMotionEvent verifiedEvent = + verifiedMotionEventFromMotionEntry(*motionEntry); + verifiedEvent.actionMasked = + dispatchEntry->resolvedAction & AMOTION_EVENT_ACTION_MASK; + verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; + std::array hmac = mHmacKeyManager.sign(verifiedEvent); // Publish the motion event. status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - INVALID_HMAC, dispatchEntry->resolvedAction, + std::move(hmac), dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, @@ -3392,7 +3402,36 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } std::unique_ptr InputDispatcher::verifyInputEvent(const InputEvent& event) { - return nullptr; + std::array calculatedHmac; + std::unique_ptr result; + switch (event.getType()) { + case AINPUT_EVENT_TYPE_KEY: { + const KeyEvent& keyEvent = static_cast(event); + VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent); + result = std::make_unique(verifiedKeyEvent); + calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent); + break; + } + case AINPUT_EVENT_TYPE_MOTION: { + const MotionEvent& motionEvent = static_cast(event); + VerifiedMotionEvent verifiedMotionEvent = + verifiedMotionEventFromMotionEvent(motionEvent); + result = std::make_unique(verifiedMotionEvent); + calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent); + break; + } + default: { + ALOGE("Cannot verify events of type %" PRId32, event.getType()); + return nullptr; + } + } + if (calculatedHmac == INVALID_HMAC) { + return nullptr; + } + if (calculatedHmac != event.getHmac()) { + return nullptr; + } + return result; } bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index ded59a595c..482133ee90 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -217,6 +217,8 @@ private: // the pointer stream in order to claim it for a system gesture. std::unordered_map> mGestureMonitorsByDisplay GUARDED_BY(mLock); + HmacKeyManager mHmacKeyManager; + // Event injection and synchronization. std::condition_variable mInjectionResultAvailable; bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c4092cd696..2fb1b65d93 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1398,6 +1398,44 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->assertNoEvents(); } +TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocus(true); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); + mDispatcher->notifyKey(&keyArgs); + + InputEvent* event = window->consume(); + ASSERT_NE(event, nullptr); + + std::unique_ptr verified = mDispatcher->verifyInputEvent(*event); + ASSERT_NE(verified, nullptr); + ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY); + + ASSERT_EQ(keyArgs.eventTime, verified->eventTimeNanos); + ASSERT_EQ(keyArgs.deviceId, verified->deviceId); + ASSERT_EQ(keyArgs.source, verified->source); + ASSERT_EQ(keyArgs.displayId, verified->displayId); + + const VerifiedKeyEvent& verifiedKey = static_cast(*verified); + + ASSERT_EQ(keyArgs.action, verifiedKey.action); + ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos); + ASSERT_EQ(keyArgs.eventTime, verifiedKey.eventTimeNanos); + ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags); + ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode); + ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode); + ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState); + ASSERT_EQ(0, verifiedKey.repeatCount); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: -- cgit v1.2.3-59-g8ed1b From 0cab12d4d25fb809ab333f03266682af97647ab7 Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Tue, 5 Nov 2019 02:17:02 +0000 Subject: Change InputReader::mDevices collection type Convert InputReader::mDevices from unordered_map of bare pointers to unordered_map of shared_ptr's. This removes the need for manual deletion of InputDevice instances and prepares for a future patch which will have multiple device ids pointing to the same InputDevice. Cherry-picked from pa/1497945. Bug: 38511270 Test: atest inputflinger_tests libinput_tests Change-Id: I0f3bfd96bfe5904ce1a8d96813e45f8467cee0fa --- services/inputflinger/reader/InputReader.cpp | 63 +++++++++++----------- services/inputflinger/reader/include/InputReader.h | 9 ++-- services/inputflinger/tests/InputReader_test.cpp | 51 +++++++++--------- 3 files changed, 61 insertions(+), 62 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 3e23fa6c84..8327ed8e30 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -62,11 +62,7 @@ InputReader::InputReader(std::shared_ptr eventHub, } // release lock } -InputReader::~InputReader() { - for (auto& devicePair : mDevices) { - delete devicePair.second; - } -} +InputReader::~InputReader() {} status_t InputReader::start() { if (mThread) { @@ -198,7 +194,8 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { uint32_t classes = mEventHub->getDeviceClasses(deviceId); int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); - InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); + std::shared_ptr device = + createDeviceLocked(deviceId, controllerNumber, identifier, classes); device->configure(when, &mConfig, 0); device->reset(when); @@ -210,7 +207,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { device->getSources()); } - mDevices.insert({deviceId, device}); + mDevices.emplace(deviceId, device); bumpGenerationLocked(); if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -225,7 +222,7 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { return; } - InputDevice* device = deviceIt->second; + std::shared_ptr device = std::move(deviceIt->second); mDevices.erase(deviceIt); bumpGenerationLocked(); @@ -242,13 +239,13 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { } device->reset(when); - delete device; } -InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes) { - InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), +std::shared_ptr InputReader::createDeviceLocked( + int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes) { + std::shared_ptr device = + std::make_shared(&mContext, deviceId, bumpGenerationLocked(), controllerNumber, identifier, classes); device->populateMappers(); return device; @@ -262,7 +259,7 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* return; } - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; if (device->isIgnored()) { // ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; @@ -273,7 +270,7 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* void InputReader::timeoutExpiredLocked(nsecs_t when) { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; if (!device->isIgnored()) { device->timeoutExpired(when); } @@ -302,7 +299,7 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { mEventHub->requestReopenDevices(); } else { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; device->configure(now, &mConfig, changes); } } @@ -313,7 +310,7 @@ void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; mGlobalMetaState |= device->getMetaState(); } } @@ -328,7 +325,7 @@ void InputReader::notifyExternalStylusPresenceChanged() { void InputReader::getExternalStylusDevicesLocked(std::vector& outDevices) { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -339,7 +336,7 @@ void InputReader::getExternalStylusDevicesLocked(std::vector& o void InputReader::dispatchExternalStylusState(const StylusState& state) { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; device->updateExternalStylusState(state); } } @@ -361,7 +358,7 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32 void InputReader::fadePointerLocked() { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; device->fadePointer(); } } @@ -386,7 +383,7 @@ void InputReader::getInputDevicesLocked(std::vector& outInputDe outInputDevices.clear(); for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; if (!device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); @@ -419,18 +416,18 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 if (deviceId >= 0) { auto deviceIt = mDevices.find(deviceId); if (deviceIt != mDevices.end()) { - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); + result = (device.get()->*getStateFunc)(sourceMask, code); } } } else { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. - int32_t currentResult = (device->*getStateFunc)(sourceMask, code); + int32_t currentResult = (device.get()->*getStateFunc)(sourceMask, code); if (currentResult >= AKEY_STATE_DOWN) { return currentResult; } else if (currentResult == AKEY_STATE_UP) { @@ -449,7 +446,7 @@ void InputReader::toggleCapsLockState(int32_t deviceId) { return; } - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; if (device->isIgnored()) { return; } @@ -472,14 +469,14 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM if (deviceId >= 0) { auto deviceIt = mDevices.find(deviceId); if (deviceIt != mDevices.end()) { - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } } else { for (auto& devicePair : mDevices) { - InputDevice* device = devicePair.second; + std::shared_ptr& device = devicePair.second; if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } @@ -506,7 +503,7 @@ void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patte AutoMutex _l(mLock); auto deviceIt = mDevices.find(deviceId); if (deviceIt != mDevices.end()) { - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; device->vibrate(pattern, patternSize, repeat, token); } } @@ -516,7 +513,7 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { auto deviceIt = mDevices.find(deviceId); if (deviceIt != mDevices.end()) { - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; device->cancelVibrate(token); } } @@ -526,7 +523,7 @@ bool InputReader::isInputDeviceEnabled(int32_t deviceId) { auto deviceIt = mDevices.find(deviceId); if (deviceIt != mDevices.end()) { - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; return device->isEnabled(); } ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); @@ -542,7 +539,7 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { return false; } - InputDevice* device = deviceIt->second; + std::shared_ptr& device = deviceIt->second; if (!device->isEnabled()) { ALOGW("Ignoring disabled device %s", device->getName().c_str()); return false; @@ -571,7 +568,7 @@ void InputReader::dump(std::string& dump) { dump += "Input Reader State:\n"; for (const auto& devicePair : mDevices) { - InputDevice* const device = devicePair.second; + const std::shared_ptr& device = devicePair.second; device->dump(dump); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index cf1af04417..4f5d2eabf3 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -84,9 +84,10 @@ public: protected: // These members are protected so they can be instrumented by test cases. - virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes); + virtual std::shared_ptr createDeviceLocked(int32_t deviceId, + int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes); // With each iteration of the loop, InputReader reads and processes one incoming message from // the EventHub. @@ -138,7 +139,7 @@ private: static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - std::unordered_map mDevices; + std::unordered_map> mDevices; // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 01bd9db76f..d870a01bfa 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1091,7 +1091,7 @@ private: // --- InstrumentedInputReader --- class InstrumentedInputReader : public InputReader { - InputDevice* mNextDevice; + std::shared_ptr mNextDevice; public: InstrumentedInputReader(std::shared_ptr eventHub, @@ -1099,33 +1099,31 @@ public: const sp& listener) : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {} - virtual ~InstrumentedInputReader() { - if (mNextDevice) { - delete mNextDevice; - } - } + virtual ~InstrumentedInputReader() {} - void setNextDevice(InputDevice* device) { mNextDevice = device; } + void setNextDevice(std::shared_ptr device) { mNextDevice = device; } - InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name, - uint32_t classes, const std::string& location = "") { + std::shared_ptr newDevice(int32_t deviceId, int32_t controllerNumber, + const std::string& name, uint32_t classes, + const std::string& location = "") { InputDeviceIdentifier identifier; identifier.name = name; identifier.location = location; int32_t generation = deviceId + 1; - return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier, - classes); + return std::make_shared(&mContext, deviceId, generation, controllerNumber, + identifier, classes); } // Make the protected loopOnce method accessible to tests. using InputReader::loopOnce; protected: - virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes) { + virtual std::shared_ptr createDeviceLocked(int32_t deviceId, + int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes) { if (mNextDevice) { - InputDevice* device = mNextDevice; + std::shared_ptr device(mNextDevice); mNextDevice = nullptr; return device; } @@ -1368,7 +1366,8 @@ protected: const std::string& name, uint32_t classes, uint32_t sources, const PropertyMap* configuration) { - InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes); + std::shared_ptr device = + mReader->newDevice(deviceId, controllerNumber, name, classes); FakeInputMapper& mapper = device->addMapper(sources); mReader->setNextDevice(device); addDevice(deviceId, name, classes, configuration); @@ -1404,7 +1403,8 @@ TEST_F(InputReaderTest, GetInputDevices) { TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { constexpr int32_t deviceId = 1; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + std::shared_ptr device = + mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); // Must add at least one mapper or the device will be ignored! device->addMapper(AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); @@ -1584,7 +1584,8 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { constexpr int32_t deviceId = 1; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + std::shared_ptr device = + mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); // Must add at least one mapper or the device will be ignored! device->addMapper(AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); @@ -1617,8 +1618,8 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { constexpr int32_t deviceId = 1; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; const char* DEVICE_LOCATION = "USB1"; - InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass, - DEVICE_LOCATION); + std::shared_ptr device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, + "fake", deviceClass, DEVICE_LOCATION); FakeInputMapper& mapper = device->addMapper(AINPUT_SOURCE_TOUCHSCREEN); mReader->setNextDevice(device); @@ -1671,7 +1672,7 @@ protected: sp mFakeListener; FakeInputReaderContext* mFakeContext; - InputDevice* mDevice; + std::shared_ptr mDevice; virtual void SetUp() override { mFakeEventHub = std::make_unique(); @@ -1683,13 +1684,13 @@ protected: InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mDevice = + std::make_shared(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } virtual void TearDown() override { - delete mDevice; - + mDevice = nullptr; delete mFakeContext; mFakeListener.clear(); mFakePolicy.clear(); -- cgit v1.2.3-59-g8ed1b From a7b82e1d297a3a5aa1150ac9556361856e681351 Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Wed, 12 Feb 2020 15:40:45 -0800 Subject: Support multiple EventHub devices per InputDevice Some physical devices contain more functions than can be addressed by a single Linux evdev device. Examples of such devices are the Sony DualShock 4 and Wacom Tablets, which appear as multiple evdev devices sharing the same value for the EVIOCGUNIQ ioctl. As more instances of such devices hit the market, apps need a way of figuring out which InputDevices are part of the same physical device. Per conversation with the android input team, a solution proposed to this problem is to merge multiple EventHub devices (which have a 1:1 relation with evdev) into a single android InputDevice. Changes: Decouple the EventHub device id ("eventHubId") from the InputDevice device id ("deviceId"). This requires InputDeviceContext to track the the EventHub devices it represents. Most logic changes are inside InputDeviceContext, so there are minimal changes to InputMappers. Added enum value END_RESERVED_ID to represent the first available id that can be assigned to a normal InputDevice. The android framework assumes specific values for the virtual keyboard and built-in hardware keyboard, so for these two cases, the "deviceId" must match the "eventHubId." Added "EVENTHUB_ID" constants to tests and changed where applicable. Cherry-picked from pa/1475694. Bug: 38511270 Test: atest inputflinger_tests libinput_tests Change-Id: I89df085fadc1c09bc999599ac5db35c9277c4a2a --- include/input/InputDevice.h | 2 + services/inputflinger/reader/InputDevice.cpp | 133 +++-- services/inputflinger/reader/InputReader.cpp | 132 ++--- services/inputflinger/reader/include/InputDevice.h | 94 +++- services/inputflinger/reader/include/InputReader.h | 22 +- services/inputflinger/tests/InputReader_test.cpp | 538 +++++++++++---------- 6 files changed, 547 insertions(+), 374 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index b6efc82fd4..20a17e3347 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -180,6 +180,8 @@ enum ReservedInputDeviceId : int32_t { VIRTUAL_KEYBOARD_ID = -1, // Device id of the "built-in" keyboard if there is one. BUILT_IN_KEYBOARD_ID = 0, + // First device id available for dynamic devices + END_RESERVED_ID = 1, }; } // namespace android diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index ae82cd4aa4..d0eee64b0c 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -18,6 +18,8 @@ #include "InputDevice.h" +#include + #include "CursorInputMapper.h" #include "ExternalStylusInputMapper.h" #include "InputReaderContext.h" @@ -32,25 +34,28 @@ namespace android { InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes) + const InputDeviceIdentifier& identifier) : mContext(context), mId(id), mGeneration(generation), - mControllerNumber(controllerNumber), + mControllerNumber(0), mIdentifier(identifier), - mClasses(classes), + mClasses(0), mSources(0), mIsExternal(false), mHasMic(false), - mDropUntilNextSync(false) { - mDeviceContext = std::make_unique(*this); -} + mDropUntilNextSync(false) {} InputDevice::~InputDevice() {} bool InputDevice::isEnabled() { - return mDeviceContext->isDeviceEnabled(); + if (!hasEventHubDevices()) { + return false; + } + // devices are either all enabled or all disabled, so we only need to check the first + auto& devicePair = mDevices.begin()->second; + auto& contextPtr = devicePair.first; + return contextPtr->isDeviceEnabled(); } void InputDevice::setEnabled(bool enabled, nsecs_t when) { @@ -65,12 +70,15 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { return; } + // When resetting some devices, the driver needs to be queried to ensure that a proper reset is + // performed. The querying must happen when the device is enabled, so we reset after enabling + // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information. if (enabled) { - mDeviceContext->enableDevice(); + for_each_subdevice([](auto& context) { context.enableDevice(); }); reset(when); } else { reset(when); - mDeviceContext->disableDevice(); + for_each_subdevice([](auto& context) { context.disableDevice(); }); } // Must change generation to flag this device as changed bumpGeneration(); @@ -118,19 +126,18 @@ void InputDevice::dump(std::string& dump) { for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); }); } -void InputDevice::populateMappers() { - uint32_t classes = mClasses; - std::vector>& mappers = mMappers; - std::unique_ptr& contextPtr = mDeviceContext; - - // External devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { - setExternal(true); +void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { + if (mDevices.find(eventHubId) != mDevices.end()) { + return; } + std::unique_ptr contextPtr(new InputDeviceContext(*this, eventHubId)); + uint32_t classes = contextPtr->getDeviceClasses(); + std::vector> mappers; - // Devices with mics. - if (classes & INPUT_DEVICE_CLASS_MIC) { - setMic(true); + // Check if we should skip population + if (!populateMappers) { + mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))}); + return; } // Switch-like devices. @@ -190,22 +197,58 @@ void InputDevice::populateMappers() { if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { mappers.push_back(std::make_unique(*contextPtr)); } + + // insert the context into the devices set + mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))}); +} + +void InputDevice::removeEventHubDevice(int32_t eventHubId) { + mDevices.erase(eventHubId); } void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { mSources = 0; + mClasses = 0; + mControllerNumber = 0; + + for_each_subdevice([this](InputDeviceContext& context) { + mClasses |= context.getDeviceClasses(); + int32_t controllerNumber = context.getDeviceControllerNumber(); + if (controllerNumber > 0) { + if (mControllerNumber && mControllerNumber != controllerNumber) { + ALOGW("InputDevice::configure(): composite device contains multiple unique " + "controller numbers"); + } + mControllerNumber = controllerNumber; + } + }); + + mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL); + mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC); if (!isIgnored()) { if (!changes) { // first time only - mDeviceContext->getConfiguration(&mConfiguration); + mConfiguration.clear(); + for_each_subdevice([this](InputDeviceContext& context) { + PropertyMap configuration; + context.getConfiguration(&configuration); + mConfiguration.addAll(&configuration); + }); } if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { sp keyboardLayout = mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mDeviceContext->setKeyboardLayoutOverlay(keyboardLayout)) { + bool shouldBumpGeneration = false; + for_each_subdevice( + [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) { + if (context.setKeyboardLayoutOverlay(keyboardLayout)) { + shouldBumpGeneration = true; + } + }); + if (shouldBumpGeneration) { bumpGeneration(); } } @@ -313,7 +356,9 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { mDropUntilNextSync = true; reset(rawEvent->when); } else { - for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); }); + for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) { + mapper.process(rawEvent); + }); } --count; } @@ -348,16 +393,20 @@ int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; - for (auto& mapperPtr : mMappers) { - InputMapper& mapper = *mapperPtr; - if (sourcesMatchMask(mapper.getSources(), sourceMask)) { - // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + InputMapper& mapper = *mapperPtr; + if (sourcesMatchMask(mapper.getSources(), sourceMask)) { + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } } } } @@ -424,11 +473,23 @@ std::optional InputDevice::getAssociatedDisplayId() { [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); }); } -InputDeviceContext::InputDeviceContext(InputDevice& device) +// returns the number of mappers associated with the device +size_t InputDevice::getMapperCount() { + size_t count = 0; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + count += mappers.size(); + } + return count; +} + +InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), mEventHub(device.getContext()->getEventHub()), - mId(device.getId()) {} + mId(eventHubId), + mDeviceId(device.getId()) {} InputDeviceContext::~InputDeviceContext() {} diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 8327ed8e30..cbfa702015 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -49,6 +49,7 @@ InputReader::InputReader(std::shared_ptr eventHub, mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1), + mNextInputDeviceId(END_RESERVED_ID), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { @@ -184,30 +185,28 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } } -void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - if (mDevices.find(deviceId) != mDevices.end()) { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); +void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { + if (mDevices.find(eventHubId) != mDevices.end()) { + ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId); return; } - InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); - - std::shared_ptr device = - createDeviceLocked(deviceId, controllerNumber, identifier, classes); + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId); + std::shared_ptr device = createDeviceLocked(eventHubId, identifier); device->configure(when, &mConfig, 0); device->reset(when); if (device->isIgnored()) { - ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, - identifier.name.c_str()); + ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' " + "(ignored non-input device)", + device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str()); } else { - ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(), + ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x", + device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(), device->getSources()); } - mDevices.emplace(deviceId, device); + mDevices.emplace(eventHubId, device); bumpGenerationLocked(); if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -215,10 +214,10 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } } -void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - auto deviceIt = mDevices.find(deviceId); +void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { + auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { - ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); + ALOGW("Ignoring spurious device removed event for eventHubId %d.", eventHubId); return; } @@ -227,35 +226,52 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { bumpGenerationLocked(); if (device->isIgnored()) { - ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), - device->getName().c_str()); + ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s' " + "(ignored non-input device)", + device->getId(), eventHubId, device->getName().c_str(), + device->getDescriptor().c_str()); } else { - ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(), - device->getName().c_str(), device->getSources()); + ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x", + device->getId(), eventHubId, device->getName().c_str(), + device->getDescriptor().c_str(), device->getSources()); } + device->removeEventHubDevice(eventHubId); + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { notifyExternalStylusPresenceChanged(); } + if (device->hasEventHubDevices()) { + device->configure(when, &mConfig, 0); + } device->reset(when); } std::shared_ptr InputReader::createDeviceLocked( - int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes) { - std::shared_ptr device = - std::make_shared(&mContext, deviceId, bumpGenerationLocked(), - controllerNumber, identifier, classes); - device->populateMappers(); + int32_t eventHubId, const InputDeviceIdentifier& identifier) { + auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) { + return devicePair.second->getDescriptor().size() && identifier.descriptor.size() && + devicePair.second->getDescriptor() == identifier.descriptor; + }); + + std::shared_ptr device; + if (deviceIt != mDevices.end()) { + device = deviceIt->second; + } else { + int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked(); + device = std::make_shared(&mContext, deviceId, bumpGenerationLocked(), + identifier); + } + device->addEventHubDevice(eventHubId); return device; } -void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, +void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count) { - auto deviceIt = mDevices.find(deviceId); + auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { - ALOGW("Discarding event for unknown deviceId %d.", deviceId); + ALOGW("Discarding event for unknown eventHubId %d.", eventHubId); return; } @@ -268,6 +284,17 @@ void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* device->process(rawEvents, count); } +InputDevice* InputReader::findInputDevice(int32_t deviceId) { + auto deviceIt = + std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) { + return devicePair.second->getId() == deviceId; + }); + if (deviceIt != mDevices.end()) { + return deviceIt->second.get(); + } + return nullptr; +} + void InputReader::timeoutExpiredLocked(nsecs_t when) { for (auto& devicePair : mDevices) { std::shared_ptr& device = devicePair.second; @@ -277,6 +304,10 @@ void InputReader::timeoutExpiredLocked(nsecs_t when) { } } +int32_t InputReader::nextInputDeviceIdLocked() { + return ++mNextInputDeviceId; +} + void InputReader::handleConfigurationChangedLocked(nsecs_t when) { // Reset global meta state because it depends on the list of all configured devices. updateGlobalMetaStateLocked(); @@ -414,12 +445,9 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 GetStateFunc getStateFunc) { int32_t result = AKEY_STATE_UNKNOWN; if (deviceId >= 0) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; - if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device.get()->*getStateFunc)(sourceMask, code); - } + InputDevice* device = findInputDevice(deviceId); + if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); } } else { for (auto& devicePair : mDevices) { @@ -440,13 +468,12 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 } void InputReader::toggleCapsLockState(int32_t deviceId) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt == mDevices.end()) { + InputDevice* device = findInputDevice(deviceId); + if (!device) { ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); return; } - std::shared_ptr& device = deviceIt->second; if (device->isIgnored()) { return; } @@ -467,12 +494,9 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM uint8_t* outFlags) { bool result = false; if (deviceId >= 0) { - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; - if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } + InputDevice* device = findInputDevice(deviceId); + if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } else { for (auto& devicePair : mDevices) { @@ -501,9 +525,8 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { device->vibrate(pattern, patternSize, repeat, token); } } @@ -511,9 +534,8 @@ void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patte void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { device->cancelVibrate(token); } } @@ -521,9 +543,8 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { bool InputReader::isInputDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt != mDevices.end()) { - std::shared_ptr& device = deviceIt->second; + InputDevice* device = findInputDevice(deviceId); + if (device) { return device->isEnabled(); } ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); @@ -533,13 +554,12 @@ bool InputReader::isInputDeviceEnabled(int32_t deviceId) { bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { AutoMutex _l(mLock); - auto deviceIt = mDevices.find(deviceId); - if (deviceIt == mDevices.end()) { + InputDevice* device = findInputDevice(deviceId); + if (!device) { ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); return false; } - std::shared_ptr& device = deviceIt->second; if (!device->isEnabled()) { ALOGW("Ignoring disabled device %s", device->getName().c_str()); return false; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 0814d1f16e..aaa0d268b3 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -17,18 +17,19 @@ #ifndef _UI_INPUTREADER_INPUT_DEVICE_H #define _UI_INPUTREADER_INPUT_DEVICE_H -#include "EventHub.h" -#include "InputReaderBase.h" -#include "InputReaderContext.h" - #include #include +#include #include -#include #include +#include #include +#include "EventHub.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" + namespace android { class InputDeviceContext; @@ -38,8 +39,7 @@ class InputMapper; class InputDevice { public: InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, - uint32_t classes); + const InputDeviceIdentifier& identifier); ~InputDevice(); inline InputReaderContext* getContext() { return mContext; } @@ -50,25 +50,25 @@ public: inline const std::string getDescriptor() { return mIdentifier.descriptor; } inline uint32_t getClasses() const { return mClasses; } inline uint32_t getSources() const { return mSources; } + inline bool hasEventHubDevices() const { return !mDevices.empty(); } inline bool isExternal() { return mIsExternal; } - inline void setExternal(bool external) { mIsExternal = external; } inline std::optional getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } inline std::optional getAssociatedViewport() const { return mAssociatedViewport; } - inline void setMic(bool hasMic) { mHasMic = hasMic; } inline bool hasMic() const { return mHasMic; } - inline bool isIgnored() { return mMappers.empty(); } + inline bool isIgnored() { return !getMapperCount(); } bool isEnabled(); void setEnabled(bool enabled, nsecs_t when); void dump(std::string& dump); - void populateMappers(); + void addEventHubDevice(int32_t eventHubId, bool populateMappers = true); + void removeEventHubDevice(int32_t eventHubId); void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); void reset(nsecs_t when); void process(const RawEvent* rawEvents, size_t count); @@ -99,11 +99,20 @@ public: std::optional getAssociatedDisplayId(); + size_t getMapperCount(); + // construct and add a mapper to the input device template - T& addMapper(Args... args) { - T* mapper = new T(*mDeviceContext, args...); - mMappers.emplace_back(mapper); + T& addMapper(int32_t eventHubId, Args... args) { + // ensure a device entry exists for this eventHubId + addEventHubDevice(eventHubId, false); + + // create mapper + auto& devicePair = mDevices[eventHubId]; + auto& deviceContext = devicePair.first; + auto& mappers = devicePair.second; + T* mapper = new T(*deviceContext, args...); + mappers.emplace_back(mapper); return *mapper; } @@ -116,8 +125,10 @@ private: std::string mAlias; uint32_t mClasses; - std::unique_ptr mDeviceContext; - std::vector> mMappers; + // map from eventHubId to device context and mappers + using MapperVector = std::vector>; + using DevicePair = std::pair, MapperVector>; + std::unordered_map mDevices; uint32_t mSources; bool mIsExternal; @@ -131,10 +142,37 @@ private: PropertyMap mConfiguration; - // run a function against every mapper + // helpers to interate over the devices collection + // run a function against every mapper on every subdevice inline void for_each_mapper(std::function f) { - for (auto& mapperPtr : mMappers) { - f(*mapperPtr); + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + f(*mapperPtr); + } + } + } + + // run a function against every mapper on a specific subdevice + inline void for_each_mapper_in_subdevice(int32_t eventHubDevice, + std::function f) { + auto deviceIt = mDevices.find(eventHubDevice); + if (deviceIt != mDevices.end()) { + auto& devicePair = deviceIt->second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + f(*mapperPtr); + } + } + } + + // run a function against every subdevice + inline void for_each_subdevice(std::function f) { + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& contextPtr = devicePair.first; + f(*contextPtr); } } @@ -142,10 +180,14 @@ private: // if all mappers return nullopt, return nullopt. template inline std::optional first_in_mappers(std::function(InputMapper&)> f) { - for (auto& mapperPtr : mMappers) { - std::optional ret = f(*mapperPtr); - if (ret) { - return ret; + for (auto& deviceEntry : mDevices) { + auto& devicePair = deviceEntry.second; + auto& mappers = devicePair.second; + for (auto& mapperPtr : mappers) { + std::optional ret = f(*mapperPtr); + if (ret) { + return ret; + } } } return std::nullopt; @@ -159,11 +201,12 @@ private: */ class InputDeviceContext { public: - InputDeviceContext(InputDevice& device); + InputDeviceContext(InputDevice& device, int32_t eventHubId); ~InputDeviceContext(); inline InputReaderContext* getContext() { return mContext; } - inline int32_t getId() { return mId; } + inline int32_t getId() { return mDeviceId; } + inline int32_t getEventHubId() { return mId; } inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); } inline InputDeviceIdentifier getDeviceIdentifier() const { @@ -259,6 +302,7 @@ private: InputReaderContext* mContext; EventHubInterface* mEventHub; int32_t mId; + int32_t mDeviceId; }; } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 4f5d2eabf3..31d82f1abe 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -84,10 +84,8 @@ public: protected: // These members are protected so they can be instrumented by test cases. - virtual std::shared_ptr createDeviceLocked(int32_t deviceId, - int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes); + virtual std::shared_ptr createDeviceLocked( + int32_t deviceId, const InputDeviceIdentifier& identifier); // With each iteration of the loop, InputReader reads and processes one incoming message from // the EventHub. @@ -139,14 +137,16 @@ private: static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - std::unordered_map> mDevices; + // An input device can represent a collection of EventHub devices. This map provides a way + // to lookup the input device instance from the EventHub device id. + std::unordered_map> mDevices; // low-level input event decoding and device management void processEventsLocked(const RawEvent* rawEvents, size_t count); - void addDeviceLocked(nsecs_t when, int32_t deviceId); - void removeDeviceLocked(nsecs_t when, int32_t deviceId); - void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); + void addDeviceLocked(nsecs_t when, int32_t eventHubId); + void removeDeviceLocked(nsecs_t when, int32_t eventHubId); + void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count); void timeoutExpiredLocked(nsecs_t when); void handleConfigurationChangedLocked(nsecs_t when); @@ -164,6 +164,9 @@ private: int32_t mGeneration; int32_t bumpGenerationLocked(); + int32_t mNextInputDeviceId; + int32_t nextInputDeviceIdLocked(); + void getInputDevicesLocked(std::vector& outInputDevices); nsecs_t mDisableVirtualKeysTimeout; @@ -182,6 +185,9 @@ private: GetStateFunc getStateFunc); bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + + // find an InputDevice from an InputDevice id + InputDevice* findInputDevice(int32_t deviceId); }; } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d870a01bfa..578605fa40 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1103,31 +1103,27 @@ public: void setNextDevice(std::shared_ptr device) { mNextDevice = device; } - std::shared_ptr newDevice(int32_t deviceId, int32_t controllerNumber, - const std::string& name, uint32_t classes, + std::shared_ptr newDevice(int32_t deviceId, const std::string& name, const std::string& location = "") { InputDeviceIdentifier identifier; identifier.name = name; identifier.location = location; int32_t generation = deviceId + 1; - return std::make_shared(&mContext, deviceId, generation, controllerNumber, - identifier, classes); + return std::make_shared(&mContext, deviceId, generation, identifier); } // Make the protected loopOnce method accessible to tests. using InputReader::loopOnce; protected: - virtual std::shared_ptr createDeviceLocked(int32_t deviceId, - int32_t controllerNumber, - const InputDeviceIdentifier& identifier, - uint32_t classes) { + virtual std::shared_ptr createDeviceLocked( + int32_t eventHubId, const InputDeviceIdentifier& identifier) { if (mNextDevice) { std::shared_ptr device(mNextDevice); mNextDevice = nullptr; return device; } - return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes); + return InputReader::createDeviceLocked(eventHubId, identifier); } friend class InputReaderTest; @@ -1338,12 +1334,12 @@ protected: mFakePolicy.clear(); } - void addDevice(int32_t deviceId, const std::string& name, uint32_t classes, - const PropertyMap* configuration) { - mFakeEventHub->addDevice(deviceId, name, classes); + void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes, + const PropertyMap* configuration) { + mFakeEventHub->addDevice(eventHubId, name, classes); if (configuration) { - mFakeEventHub->addConfigurationMap(deviceId, configuration); + mFakeEventHub->addConfigurationMap(eventHubId, configuration); } mFakeEventHub->finishDeviceScan(); mReader->loopOnce(); @@ -1362,15 +1358,14 @@ protected: mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE); } - FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, + FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId, const std::string& name, uint32_t classes, uint32_t sources, const PropertyMap* configuration) { - std::shared_ptr device = - mReader->newDevice(deviceId, controllerNumber, name, classes); - FakeInputMapper& mapper = device->addMapper(sources); + std::shared_ptr device = mReader->newDevice(deviceId, name); + FakeInputMapper& mapper = device->addMapper(eventHubId, sources); mReader->setNextDevice(device); - addDevice(deviceId, name, classes, configuration); + addDevice(eventHubId, name, classes, configuration); return mapper; } }; @@ -1384,7 +1379,7 @@ TEST_F(InputReaderTest, GetInputDevices) { std::vector inputDevices; mReader->getInputDevices(inputDevices); ASSERT_EQ(1U, inputDevices.size()); - ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); @@ -1393,7 +1388,7 @@ TEST_F(InputReaderTest, GetInputDevices) { // Should also have received a notification describing the new input devices. inputDevices = mFakePolicy->getInputDevices(); ASSERT_EQ(1U, inputDevices.size()); - ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); @@ -1401,14 +1396,14 @@ TEST_F(InputReaderTest, GetInputDevices) { } TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - std::shared_ptr device = - mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! - device->addMapper(AINPUT_SOURCE_KEYBOARD); + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); @@ -1438,8 +1433,11 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { } TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); @@ -1447,13 +1445,16 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, AKEYCODE_A)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + AKEYCODE_A)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) @@ -1465,8 +1466,11 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN); @@ -1474,13 +1478,16 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, KEY_A)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getScanCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, KEY_A)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getScanCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + KEY_A)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1, AINPUT_SOURCE_TRACKBALL, KEY_A)) @@ -1492,8 +1499,11 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN); @@ -1501,13 +1511,16 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { AINPUT_SOURCE_ANY, SW_LID)) << "Should return unknown when the device id is >= 0 but unknown."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getSwitchState(deviceId, AINPUT_SOURCE_TRACKBALL, SW_LID)) + << "Should return unknown when the device id is valid but the sources are not " + "supported by the device."; - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getSwitchState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, + SW_LID)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1, AINPUT_SOURCE_TRACKBALL, SW_LID)) @@ -1519,8 +1532,11 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); mapper.addSupportedKeyCode(AKEYCODE_A); @@ -1534,13 +1550,16 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when device id is valid but the sources are not supported by the device."; + ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) + << "Should return false when device id is valid but the sources are not supported by " + "the device."; ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; + ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, + keyCodes, flags)) + << "Should return value provided by mapper when device id is valid and the device " + "supports some of the sources."; ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); flags[3] = 1; @@ -1555,7 +1574,8 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); + constexpr int32_t eventHubId = 1; + addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1564,32 +1584,35 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange } TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = - addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD, + addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, AINPUT_SOURCE_KEYBOARD, nullptr); - mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); + mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); RawEvent event; ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event)); ASSERT_EQ(0, event.when); - ASSERT_EQ(1, event.deviceId); + ASSERT_EQ(eventHubId, event.deviceId); ASSERT_EQ(EV_KEY, event.type); ASSERT_EQ(KEY_A, event.code); ASSERT_EQ(1, event.value); } TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - std::shared_ptr device = - mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass); + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! - device->addMapper(AINPUT_SOURCE_KEYBOARD); + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); mReader->setNextDevice(device); - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -1615,12 +1638,13 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { } TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { - constexpr int32_t deviceId = 1; + constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; const char* DEVICE_LOCATION = "USB1"; - std::shared_ptr device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, - "fake", deviceClass, DEVICE_LOCATION); - FakeInputMapper& mapper = device->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + std::shared_ptr device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); + FakeInputMapper& mapper = + device->addMapper(eventHubId, AINPUT_SOURCE_TOUCHSCREEN); mReader->setNextDevice(device); const uint8_t hdmi1 = 1; @@ -1640,7 +1664,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Add the device, and make sure all of the callbacks are triggered. // The device is added after the input port associations are processed since // we do not yet support dynamic device-to-display associations. - ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); @@ -1666,6 +1690,7 @@ protected: static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; + static const int32_t EVENTHUB_ID; std::shared_ptr mFakeEventHub; sp mFakePolicy; @@ -1680,13 +1705,12 @@ protected: mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); - mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); + mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = - std::make_shared(mFakeContext, DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mDevice = std::make_shared(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + identifier); } virtual void TearDown() override { @@ -1699,20 +1723,21 @@ protected: const char* InputDeviceTest::DEVICE_NAME = "device"; const char* InputDeviceTest::DEVICE_LOCATION = "USB1"; -const int32_t InputDeviceTest::DEVICE_ID = 1; +const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK; +const int32_t InputDeviceTest::EVENTHUB_ID = 1; TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(DEVICE_ID, mDevice->getId()); ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str()); - ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses()); + ASSERT_EQ(0U, mDevice->getClasses()); } -TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsTrue) { - ASSERT_EQ(mDevice->isEnabled(), true); +TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { + ASSERT_EQ(mDevice->isEnabled(), false); } TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { @@ -1759,9 +1784,10 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { // Configuration. - mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value")); + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value")); - FakeInputMapper& mapper1 = mDevice->addMapper(AINPUT_SOURCE_KEYBOARD); + FakeInputMapper& mapper1 = + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); mapper1.setMetaState(AMETA_ALT_ON); mapper1.addSupportedKeyCode(AKEYCODE_A); @@ -1772,7 +1798,8 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe mapper1.setScanCodeState(3, AKEY_STATE_UP); mapper1.setSwitchState(4, AKEY_STATE_DOWN); - FakeInputMapper& mapper2 = mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + FakeInputMapper& mapper2 = + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN); mapper2.setMetaState(AMETA_SHIFT_ON); InputReaderConfiguration config; @@ -1843,6 +1870,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // Event handling. RawEvent event; + event.deviceId = EVENTHUB_ID; mDevice->process(&event, 1); ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled()); @@ -1853,7 +1881,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // 1. Device is disabled if the viewport corresponding to the associated display is not found // 2. Device is disabled when setEnabled API is called TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { - mDevice->addMapper(AINPUT_SOURCE_TOUCHSCREEN); + mDevice->addMapper(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN); // First Configuration. mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); @@ -1902,6 +1930,7 @@ protected: static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; + static const int32_t EVENTHUB_ID; std::shared_ptr mFakeEventHub; sp mFakePolicy; @@ -1909,7 +1938,7 @@ protected: FakeInputReaderContext* mFakeContext; InputDevice* mDevice; - virtual void SetUp() override { + virtual void SetUp(uint32_t classes) { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1917,12 +1946,13 @@ protected: InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier); - mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); + mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes); } + virtual void SetUp() override { SetUp(DEVICE_CLASSES); } + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1931,7 +1961,7 @@ protected: } void addConfigurationProperty(const char* key, const char* value) { - mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value)); + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value)); } void configureDevice(uint32_t changes) { @@ -1940,7 +1970,7 @@ protected: template T& addMapperAndConfigure(Args... args) { - T& mapper = mDevice->addMapper(args...); + T& mapper = mDevice->addMapper(EVENTHUB_ID, args...); configureDevice(0); mDevice->reset(ARBITRARY_TIME); return mapper; @@ -1962,7 +1992,7 @@ protected: int32_t value) { RawEvent event; event.when = when; - event.deviceId = mapper.getDeviceId(); + event.deviceId = mapper.getDeviceContext().getEventHubId(); event.type = type; event.code = code; event.value = value; @@ -2007,11 +2037,11 @@ protected: const char* InputMapperTest::DEVICE_NAME = "device"; const char* InputMapperTest::DEVICE_LOCATION = "USB1"; -const int32_t InputMapperTest::DEVICE_ID = 1; +const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputMapperTest::DEVICE_GENERATION = 2; const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0; const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests - +const int32_t InputMapperTest::EVENTHUB_ID = 1; // --- SwitchInputMapperTest --- @@ -2028,10 +2058,10 @@ TEST_F(SwitchInputMapperTest, GetSources) { TEST_F(SwitchInputMapperTest, GetSwitchState) { SwitchInputMapper& mapper = addMapperAndConfigure(); - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1); + mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1); ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0); + mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0); ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); } @@ -2105,8 +2135,8 @@ TEST_F(KeyboardInputMapperTest, GetSources) { TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { const int32_t USAGE_A = 0x070004; const int32_t USAGE_UNKNOWN = 0x07ffff; - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2203,8 +2233,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { } TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2242,10 +2272,10 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { } TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2263,10 +2293,10 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD } TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = @@ -2339,7 +2369,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) { // If the keyboard is not orientation aware, // key events should not be associated with a specific display id - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2364,7 +2394,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { // If the keyboard is orientation aware, // key events should be associated with the internal viewport - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); addConfigurationProperty("keyboard.orientationAware", "1"); KeyboardInputMapper& mapper = @@ -2399,10 +2429,10 @@ TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1); + mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1); ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0); + mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0); ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); } @@ -2411,10 +2441,10 @@ TEST_F(KeyboardInputMapperTest, GetScanCodeState) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1); + mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1); ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0); + mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0); ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); } @@ -2423,7 +2453,7 @@ TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; uint8_t flags[2] = { 0, 0 }; @@ -2433,99 +2463,100 @@ TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { } TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) { - mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(DEVICE_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); // Initialization should have turned all of the lights off. - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); // Toggle caps lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); // Toggle num lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle caps lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock on. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle num lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock off. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); } TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { // keyboard 1. - mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); // keyboard 2. const std::string USB2 = "USB2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; InputDeviceIdentifier identifier; identifier.name = "KEYBOARD2"; identifier.location = USB2; std::unique_ptr device2 = std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); - mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + identifier); + mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); KeyboardInputMapper& mapper2 = - device2->addMapper(AINPUT_SOURCE_KEYBOARD, + device2->addMapper(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -2577,14 +2608,23 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { AKEYCODE_DPAD_LEFT, newDisplayId)); } -TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { +// --- KeyboardInputMapperTest_ExternalDevice --- + +class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { +protected: + virtual void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); + } +}; + +TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { // For external devices, non-media keys will trigger wake on key down. Media keys need to be // marked as WAKE in the keylayout file to trigger wake. - mDevice->setExternal(true); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, + POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = addMapperAndConfigure(AINPUT_SOURCE_KEYBOARD, @@ -2616,13 +2656,12 @@ TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) { +TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { // Tv Remote key's wake behavior is prescribed by the keylayout file. - mDevice->setExternal(true); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); KeyboardInputMapper& mapper = @@ -3564,10 +3603,10 @@ void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) { } void TouchInputMapperTest::prepareVirtualKeys() { - mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]); - mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE); + mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[0]); + mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[1]); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE); } void TouchInputMapperTest::prepareLocationCalibration() { @@ -3628,33 +3667,29 @@ protected: }; void SingleTouchInputMapperTest::prepareButtons() { - mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); } void SingleTouchInputMapperTest::prepareAxes(int axes) { if (axes & POSITION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X, - RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y, - RAW_Y_MIN, RAW_Y_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); } if (axes & PRESSURE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, + RAW_PRESSURE_MAX, 0, 0); } if (axes & TOOL) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH, - RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, + 0); } if (axes & DISTANCE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE, - RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_DISTANCE, RAW_DISTANCE_MIN, + RAW_DISTANCE_MAX, 0, 0); } if (axes & TILT) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_X, - RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_Y, - RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_X, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_Y, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0); } } @@ -3710,8 +3745,8 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNot } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { - mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); - mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); + mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X); + mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y); prepareButtons(); prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure(); @@ -4778,7 +4813,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueI prepareDisplay(DISPLAY_ORIENTATION_0); prepareButtons(); prepareAxes(POSITION); - mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0); SingleTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -4940,51 +4975,47 @@ protected: void MultiTouchInputMapperTest::prepareAxes(int axes) { if (axes & POSITION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X, - RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y, - RAW_Y_MIN, RAW_Y_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); } if (axes & TOUCH) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, + RAW_TOUCH_MAX, 0, 0); if (axes & MINOR) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, RAW_TOUCH_MIN, + RAW_TOUCH_MAX, 0, 0); } } if (axes & TOOL) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, - RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, + 0, 0); if (axes & MINOR) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR, - RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MAX, + RAW_TOOL_MAX, 0, 0); } } if (axes & ORIENTATION) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION, - RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, RAW_ORIENTATION_MIN, + RAW_ORIENTATION_MAX, 0, 0); } if (axes & PRESSURE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, RAW_PRESSURE_MIN, + RAW_PRESSURE_MAX, 0, 0); } if (axes & DISTANCE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE, - RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_DISTANCE, RAW_DISTANCE_MIN, + RAW_DISTANCE_MAX, 0, 0); } if (axes & ID) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID, - RAW_ID_MIN, RAW_ID_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, 0, + 0); } if (axes & SLOT) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT, - RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0); - mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0); + mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, 0); } if (axes & TOOL_TYPE) { - mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); } } @@ -6273,7 +6304,7 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); - mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); MultiTouchInputMapper& mapper = addMapperAndConfigure(); NotifyMotionArgs motionArgs; @@ -6452,35 +6483,6 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { ASSERT_EQ(DISPLAY_ID, args.displayId); } -/** - * Expect fallback to internal viewport if device is external and external viewport is not present. - */ -TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) { - prepareAxes(POSITION); - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - mDevice->setExternal(true); - MultiTouchInputMapper& mapper = addMapperAndConfigure(); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); - - NotifyMotionArgs motionArgs; - - // Expect the event to be sent to the internal viewport, - // because an external viewport is not present. - processPosition(mapper, 100, 100); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId); - - // Expect the event to be sent to the external viewport if it is present. - prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); - processPosition(mapper, 100, 100); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); -} - TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { // Setup for second display. sp fakePointerController = new FakePointerController(); @@ -6517,27 +6519,28 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; InputDeviceIdentifier identifier; identifier.name = "TOUCHSCREEN2"; identifier.location = USB2; std::unique_ptr device2 = std::make_unique(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); - mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, - 0 /*flat*/, 0 /*fuzz*/); - mFakeEventHub->setAbsoluteAxisValue(SECOND_DEVICE_ID, ABS_MT_SLOT, 0 /*value*/); - mFakeEventHub->addConfigurationProperty(SECOND_DEVICE_ID, String8("touch.deviceType"), - String8("touchScreen")); + identifier); + mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, + 0 /*flat*/, 0 /*fuzz*/); + mFakeEventHub->setAbsoluteAxisValue(SECOND_EVENTHUB_ID, ABS_MT_SLOT, 0 /*value*/); + mFakeEventHub->addConfigurationProperty(SECOND_EVENTHUB_ID, String8("touch.deviceType"), + String8("touchScreen")); // Setup the second touch screen device. - MultiTouchInputMapper& mapper2 = device2->addMapper(); + MultiTouchInputMapper& mapper2 = device2->addMapper(SECOND_EVENTHUB_ID); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -6598,7 +6601,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { // Unrotated video frame TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); std::vector frames{frame}; - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6628,7 +6631,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { clearViewports(); prepareDisplay(orientation); std::vector frames{frame}; - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6650,7 +6653,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { NotifyMotionArgs motionArgs; prepareDisplay(DISPLAY_ORIENTATION_90); - mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}}); processPosition(mapper, 100, 200); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -6824,4 +6827,41 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); } +// --- MultiTouchInputMapperTest_ExternalDevice --- + +class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { +protected: + virtual void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); + } +}; + +/** + * Expect fallback to internal viewport if device is external and external viewport is not present. + */ +TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); + + NotifyMotionArgs motionArgs; + + // Expect the event to be sent to the internal viewport, + // because an external viewport is not present. + processPosition(mapper, 100, 100); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId); + + // Expect the event to be sent to the external viewport if it is present. + prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); + processPosition(mapper, 100, 100); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From c7ef27eeccb801ed6d82b62a1d743f246f66eec9 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Mon, 3 Feb 2020 19:19:15 -0800 Subject: Let InputReaderContext hold a single PointerController InputMappers obtain the pointer controller directly from the policy, which, when we support multiple pointer controller, makes it natural to have one pointer controller for an input device. Rather than that, we want to build a system that makes it more natural to have one pointer per display. To give more control over the management of pointers, we make it so that InputReaderContext is responsible for managing the pointer controller. Bug: 146385350 Test: atest inputflinger_tests Change-Id: I0b9276ed4a7f55f04f910dd484ecc7f767b8723b --- services/inputflinger/reader/InputDevice.cpp | 4 -- services/inputflinger/reader/InputReader.cpp | 48 ++++++++++++++++++++-- services/inputflinger/reader/include/InputDevice.h | 2 - services/inputflinger/reader/include/InputReader.h | 6 +++ .../reader/include/InputReaderContext.h | 2 + .../reader/mapper/CursorInputMapper.cpp | 32 +-------------- .../inputflinger/reader/mapper/CursorInputMapper.h | 3 -- .../inputflinger/reader/mapper/InputMapper.cpp | 2 - services/inputflinger/reader/mapper/InputMapper.h | 1 - .../reader/mapper/TouchInputMapper.cpp | 17 ++------ .../inputflinger/reader/mapper/TouchInputMapper.h | 1 - services/inputflinger/tests/InputReader_test.cpp | 26 ++++++++++++ 12 files changed, 83 insertions(+), 61 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index d0eee64b0c..f2afc818ff 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -449,10 +449,6 @@ void InputDevice::updateMetaState(int32_t keyCode) { for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); }); } -void InputDevice::fadePointer() { - for_each_mapper([](InputMapper& mapper) { mapper.fadePointer(); }); -} - void InputDevice::bumpGeneration() { mGeneration = mContext->bumpGeneration(); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index cbfa702015..6ab5902bb7 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -326,6 +326,10 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { InputReaderConfiguration::changesToString(changes).c_str()); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) { + updatePointerDisplayLocked(); + } + if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); } else { @@ -387,10 +391,43 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32 } } +sp InputReader::getPointerControllerLocked(int32_t deviceId) { + sp controller = mPointerController.promote(); + if (controller == nullptr) { + controller = mPolicy->obtainPointerController(deviceId); + mPointerController = controller; + updatePointerDisplayLocked(); + } + return controller; +} + +void InputReader::updatePointerDisplayLocked() { + sp controller = mPointerController.promote(); + if (controller == nullptr) { + return; + } + + std::optional viewport = + mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); + if (!viewport) { + ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input " + "mapper. Fall back to default display", + mConfig.defaultPointerDisplayId); + viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT); + } + if (!viewport) { + ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to" + " PointerController."); + return; + } + + controller->setDisplayViewport(*viewport); +} + void InputReader::fadePointerLocked() { - for (auto& devicePair : mDevices) { - std::shared_ptr& device = devicePair.second; - device->fadePointer(); + sp controller = mPointerController.promote(); + if (controller != nullptr) { + controller->fade(PointerControllerInterface::TRANSITION_GRADUAL); } } @@ -688,6 +725,11 @@ void InputReader::ContextImpl::fadePointer() { mReader->fadePointerLocked(); } +sp InputReader::ContextImpl::getPointerController(int32_t deviceId) { + // lock is already held by the input loop + return mReader->getPointerControllerLocked(deviceId); +} + void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { // lock is already held by the input loop mReader->requestTimeoutAtTimeLocked(when); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index aaa0d268b3..71313fc86f 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -88,8 +88,6 @@ public: int32_t getMetaState(); void updateMetaState(int32_t keyCode); - void fadePointer(); - void bumpGeneration(); void notifyReset(nsecs_t when); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 31d82f1abe..ca1a081e48 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -23,6 +23,7 @@ #include "InputReaderContext.h" #include "InputThread.h" +#include #include #include @@ -102,6 +103,7 @@ protected: virtual void disableVirtualKeysUntil(nsecs_t time) override; virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; virtual void fadePointer() override; + virtual sp getPointerController(int32_t deviceId) override; virtual void requestTimeoutAtTime(nsecs_t when) override; virtual int32_t bumpGeneration() override; virtual void getExternalStylusDevices(std::vector& outDevices) override; @@ -159,6 +161,10 @@ private: void getExternalStylusDevicesLocked(std::vector& outDevices); void dispatchExternalStylusState(const StylusState& state); + // The PointerController that is shared among all the input devices that need it. + wp mPointerController; + sp getPointerControllerLocked(int32_t deviceId); + void updatePointerDisplayLocked(); void fadePointerLocked(); int32_t mGeneration; diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index e14fbbec3d..d5527cf926 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -28,6 +28,7 @@ class InputDevice; class InputListenerInterface; class InputMapper; class InputReaderPolicyInterface; +class PointerControllerInterface; struct StylusState; /* Internal interface used by individual input devices to access global input device state @@ -45,6 +46,7 @@ public: virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0; virtual void fadePointer() = 0; + virtual sp getPointerController(int32_t deviceId) = 0; virtual void requestTimeoutAtTime(nsecs_t when) = 0; virtual int32_t bumpGeneration() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 78f33823d0..c74987dfec 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -132,7 +132,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mYPrecision = 1.0f; mXScale = 1.0f; mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + mPointerController = getContext()->getPointerController(getDeviceId()); break; case Parameters::MODE_NAVIGATION: mSource = AINPUT_SOURCE_TRACKBALL; @@ -189,34 +189,10 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } } - // Update the PointerController if viewports changed. - if (mParameters.mode == Parameters::MODE_POINTER) { - updatePointerControllerDisplayViewport(*config); - } bumpGeneration(); } } -void CursorInputMapper::updatePointerControllerDisplayViewport( - const InputReaderConfiguration& config) { - std::optional viewport = - config.getDisplayViewportById(config.defaultPointerDisplayId); - if (!viewport) { - ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input " - "mapper. Fall back to default display", - config.defaultPointerDisplayId); - viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT); - } - - if (!viewport) { - ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to" - " PointerController."); - return; - } - - mPointerController->setDisplayViewport(*viewport); -} - void CursorInputMapper::configureParameters() { mParameters.mode = Parameters::MODE_POINTER; String8 cursorModeString; @@ -481,12 +457,6 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } } -void CursorInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - std::optional CursorInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { if (mParameters.mode == Parameters::MODE_POINTER) { diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 94ab30674d..f65ac3934a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -66,8 +66,6 @@ public: virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - virtual void fadePointer() override; - virtual std::optional getAssociatedDisplayId() override; private: @@ -117,7 +115,6 @@ private: void dumpParameters(std::string& dump); void sync(nsecs_t when); - void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 92af6123cc..a5b98967e7 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -71,8 +71,6 @@ void InputMapper::updateMetaState(int32_t keyCode) {} void InputMapper::updateExternalStylusState(const StylusState& state) {} -void InputMapper::fadePointer() {} - status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo); } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 09888bfe20..949c7ea34e 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -71,7 +71,6 @@ public: virtual void updateExternalStylusState(const StylusState& state); - virtual void fadePointer(); virtual std::optional getAssociatedDisplayId() { return std::nullopt; } protected: diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index e832804e16..9cbbf0c376 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -756,16 +756,11 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.clear(); } - // Create or update pointer controller if needed. + // Create pointer controller if needed. if (mDeviceMode == DEVICE_MODE_POINTER || (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == nullptr || viewportChanged) { - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - // Set the DisplayViewport for the PointerController to the default pointer display - // that is recommended by the configuration before using it. - std::optional defaultViewport = - mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); - mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport)); + if (mPointerController == nullptr) { + mPointerController = getContext()->getPointerController(getDeviceId()); } } else { mPointerController.clear(); @@ -3624,12 +3619,6 @@ bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties return changed; } -void TouchInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - void TouchInputMapper::cancelTouch(nsecs_t when) { abortPointerUsage(when, 0 /*policyFlags*/); abortTouches(when, 0 /* policyFlags*/); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 3a61206481..e21a33abed 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -148,7 +148,6 @@ public: virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) override; - virtual void fadePointer() override; virtual void cancelTouch(nsecs_t when) override; virtual void timeoutExpired(nsecs_t when) override; virtual void updateExternalStylusState(const StylusState& state) override; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 578605fa40..752213a499 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -830,6 +830,7 @@ class FakeInputReaderContext : public InputReaderContext { bool mUpdateGlobalMetaStateWasCalled; int32_t mGeneration; uint32_t mNextSequenceNum; + wp mPointerController; public: FakeInputReaderContext(std::shared_ptr eventHub, @@ -857,6 +858,18 @@ public: return mGeneration; } + void updatePointerDisplay() { + sp controller = mPointerController.promote(); + if (controller != nullptr) { + InputReaderConfiguration config; + mPolicy->getReaderConfiguration(&config); + auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId); + if (viewport) { + controller->setDisplayViewport(*viewport); + } + } + } + private: virtual void updateGlobalMetaState() { mUpdateGlobalMetaStateWasCalled = true; @@ -883,6 +896,16 @@ private: virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; } + virtual sp getPointerController(int32_t deviceId) { + sp controller = mPointerController.promote(); + if (controller == nullptr) { + controller = mPolicy->obtainPointerController(deviceId); + mPointerController = controller; + updatePointerDisplay(); + } + return controller; + } + virtual void fadePointer() { } @@ -1965,6 +1988,9 @@ protected: } void configureDevice(uint32_t changes) { + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mFakeContext->updatePointerDisplay(); + } mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); } -- cgit v1.2.3-59-g8ed1b From 9244aeaf7ba06fbbcdddfda20abe3b6d89004cb8 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 5 Feb 2020 20:31:40 -0800 Subject: Use cc_defaults in inputflinger targets We add a cc_defaults for all of the targets in inputflinger, and include those defaults in inputflinger_tests. This makes it so that building the test compiles all of the sources in the test target and includes them in the executable. The primary benefit of this refactor is that running the test on any device will now test the inputflinger code that the test was compiled with instead of testing the libraries that were on the device. Also, the device does not need to be re-flashed or need its libraries to be updated when running the tests after modifying inputflinger code. Bug: None Test: atest inputflinger_tests Change-Id: Ic0406355e5b485c8bf29a65f7c7c37ea8cb68eab --- services/inputflinger/Android.bp | 72 ++++++++++++++-------- services/inputflinger/dispatcher/Android.bp | 41 +++++++++--- services/inputflinger/reader/Android.bp | 31 +++++++--- .../reader/mapper/CursorInputMapper.cpp | 2 +- .../reader/mapper/ExternalStylusInputMapper.cpp | 2 +- .../inputflinger/reader/mapper/InputMapper.cpp | 2 +- .../reader/mapper/JoystickInputMapper.cpp | 2 +- .../reader/mapper/KeyboardInputMapper.cpp | 2 +- .../reader/mapper/MultiTouchInputMapper.cpp | 2 +- .../reader/mapper/RotaryEncoderInputMapper.cpp | 2 +- .../reader/mapper/SwitchInputMapper.cpp | 2 +- .../reader/mapper/TouchInputMapper.cpp | 2 +- .../reader/mapper/VibratorInputMapper.cpp | 2 +- services/inputflinger/reporter/Android.bp | 24 +++++--- services/inputflinger/tests/Android.bp | 52 ++++++++-------- services/inputflinger/tests/InputReader_test.cpp | 2 +- 16 files changed, 154 insertions(+), 88 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 5c80d551b8..439d9bf312 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Default flags to be used throughout all libraries in inputflinger. cc_defaults { name: "inputflinger_defaults", cflags: [ @@ -23,22 +24,25 @@ cc_defaults { ], } -cc_library_shared { - name: "libinputflinger", - defaults: ["inputflinger_defaults"], +///////////////////////////////////////////////// +// libinputflinger +///////////////////////////////////////////////// +filegroup { + name: "libinputflinger_sources", srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", "InputManager.cpp", ], +} +cc_defaults { + name: "libinputflinger_defaults", + srcs: [":libinputflinger_sources"], shared_libs: [ "android.hardware.input.classifier@1.0", "libbase", - "libinputflinger_base", - "libinputreporter", - "libinputreader", "libbinder", "libcrypto", "libcutils", @@ -50,60 +54,76 @@ cc_library_shared { "libui", "server_configurable_flags", ], +} - static_libs: [ - "libinputdispatcher", +cc_library_shared { + name: "libinputflinger", + defaults: [ + "inputflinger_defaults", + "libinputflinger_defaults", ], - cflags: [ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden ], - - export_include_dirs: [ - ".", - "include", + shared_libs: [ + // This should consist only of dependencies from inputflinger. Other dependencies should be + // in cc_defaults so that they are included in the tests. + "libinputflinger_base", + "libinputreporter", + "libinputreader", + ], + static_libs: [ + "libinputdispatcher", ], - export_static_lib_headers: [ "libinputdispatcher", ], + export_include_dirs: [ + ".", + "include", + ], } +///////////////////////////////////////////////// +// libinputflinger_base +///////////////////////////////////////////////// + cc_library_headers { name: "libinputflinger_headers", - header_libs: ["libinputreporter_headers"], export_include_dirs: ["include"], - export_header_lib_headers: ["libinputreporter_headers"], } -cc_library_shared { - name: "libinputflinger_base", - defaults: ["inputflinger_defaults"], - +filegroup { + name: "libinputflinger_base_sources", srcs: [ "InputListener.cpp", "InputReaderBase.cpp", "InputThread.cpp", ], +} +cc_defaults { + name: "libinputflinger_base_defaults", + srcs: [":libinputflinger_base_sources"], shared_libs: [ "libbase", "libinput", "liblog", "libutils", ], - header_libs: [ "libinputflinger_headers", ], +} +cc_library_shared { + name: "libinputflinger_base", + defaults: [ + "inputflinger_defaults", + "libinputflinger_base_defaults", + ], export_header_lib_headers: [ "libinputflinger_headers", ], } - -subdirs = [ - "host", - "tests", -] diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 3f956a88fb..a98f4b42d0 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -12,9 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_static { - name: "libinputdispatcher", - defaults: ["inputflinger_defaults"], +cc_library_headers { + name: "libinputdispatcher_headers", + export_include_dirs: [ + "include", + ], +} + +filegroup { + name: "libinputdispatcher_sources", srcs: [ "Connection.cpp", "Entry.cpp", @@ -24,20 +30,41 @@ cc_library_static { "InputState.cpp", "InputTarget.cpp", "Monitor.cpp", - "TouchState.cpp" + "TouchState.cpp", ], +} + +cc_defaults { + name: "libinputdispatcher_defaults", + srcs: [":libinputdispatcher_sources"], shared_libs: [ "libbase", "libcrypto", "libcutils", "libinput", - "libinputreporter", - "libinputflinger_base", "liblog", "libstatslog", "libui", "libutils", ], + header_libs: [ + "libinputdispatcher_headers", + ], +} - export_include_dirs: ["include"], +cc_library_static { + name: "libinputdispatcher", + defaults: [ + "inputflinger_defaults", + "libinputdispatcher_defaults", + ], + shared_libs: [ + // This should consist only of dependencies from inputflinger. Other dependencies should be + // in cc_defaults so that they are included in the tests. + "libinputreporter", + "libinputflinger_base", + ], + export_header_lib_headers: [ + "libinputdispatcher_headers", + ], } diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 23c08b25f3..83a610f768 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -21,10 +21,8 @@ cc_library_headers { ], } -cc_library_shared { - name: "libinputreader", - defaults: ["inputflinger_defaults"], - +filegroup { + name: "libinputreader_sources", srcs: [ "EventHub.cpp", "InputDevice.cpp", @@ -44,14 +42,16 @@ cc_library_shared { "mapper/TouchInputMapper.cpp", "mapper/VibratorInputMapper.cpp", "InputReader.cpp", - "InputReaderFactory.cpp", "TouchVideoDevice.cpp", ], +} +cc_defaults { + name: "libinputreader_defaults", + srcs: [":libinputreader_sources"], shared_libs: [ "libbase", "libcap", - "libinputflinger_base", "libcrypto", "libcutils", "libinput", @@ -59,13 +59,26 @@ cc_library_shared { "libui", "libutils", ], - header_libs: [ - "libinputflinger_headers", "libinputreader_headers", ], +} +cc_library_shared { + name: "libinputreader", + defaults: [ + "inputflinger_defaults", + "libinputreader_defaults" + ], + srcs: [ + "InputReaderFactory.cpp", + ], + shared_libs: [ + // This should consist only of dependencies from inputflinger. Other dependencies should be + // in cc_defaults so that they are included in the tests. + "libinputflinger_base", + ], export_header_lib_headers: [ - "libinputflinger_headers", + "libinputreader_headers", ], } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 78f33823d0..520a4a4f52 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "CursorInputMapper.h" diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 37e4047890..37d1d7499c 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "ExternalStylusInputMapper.h" diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 92af6123cc..c5f99c9b63 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "InputMapper.h" diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 57c85b6280..7c3b1d9b40 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "JoystickInputMapper.h" diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 9ab707fb60..ab354a2e2b 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "KeyboardInputMapper.h" diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index d195a07eff..d77c8c8609 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "MultiTouchInputMapper.h" diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index a1cce5695b..ed93f14cea 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "RotaryEncoderInputMapper.h" diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 52b24498ac..e79aeb28c4 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "SwitchInputMapper.h" diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index e832804e16..b3a6df8f15 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "TouchInputMapper.h" diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 1b584ea7b9..7665680feb 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Macros.h" +#include "../Macros.h" #include "VibratorInputMapper.h" diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index 5956fb0794..fbc51dadc0 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -17,25 +17,33 @@ cc_library_headers { export_include_dirs: ["."], } -cc_library_shared { - name: "libinputreporter", - defaults: ["inputflinger_defaults"], - +filegroup { + name: "libinputreporter_sources", srcs: [ - "InputReporter.cpp", + "InputReporter.cpp", ], +} +cc_defaults { + name: "libinputreporter_defaults", + srcs: [":libinputreporter_sources"], shared_libs: [ "liblog", "libutils", ], - header_libs: [ - "libinputflinger_headers", + "libinputreporter_headers", ], +} +cc_library_shared { + name: "libinputreporter", + defaults: [ + "inputflinger_defaults", + "libinputreporter_defaults", + ], export_header_lib_headers: [ - "libinputflinger_headers", + "libinputreporter_headers", ], } diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index f913d826bc..73d22727f0 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -1,7 +1,31 @@ -// Build the unit tests. +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. cc_test { name: "inputflinger_tests", + defaults: [ + "inputflinger_defaults", + // For all targets inside inputflinger, these tests build all of their sources using their + // defaults rather than including them as shared or static libraries. By doing so, the tests + // will always run against the compiled version of the inputflinger code rather than the + // version on the device. + "libinputflinger_base_defaults", + "libinputreader_defaults", + "libinputreporter_defaults", + "libinputdispatcher_defaults", + "libinputflinger_defaults", + ], srcs: [ "BlockingQueue_test.cpp", "EventHub_test.cpp", @@ -12,31 +36,5 @@ cc_test { "InputReader_test.cpp", "UinputDevice.cpp", ], - cflags: [ - "-Wall", - "-Werror", - "-Wextra", - "-Wno-unused-parameter", - "-Wthread-safety", - ], - shared_libs: [ - "android.hardware.input.classifier@1.0", - "libbase", - "libbinder", - "libcutils", - "liblog", - "libutils", - "libhardware", - "libhardware_legacy", - "libui", - "libinput", - "libinputflinger", - "libinputreader", - "libinputflinger_base", - "libinputservice", - ], - header_libs: [ - "libinputreader_headers", - ], require_root: true, } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index f9ebfc07b2..af11256892 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1714,7 +1714,7 @@ protected: mFakePolicy = new FakeInputReaderPolicy(); mTestListener = new TestInputListener(); - mReader = createInputReader(mFakePolicy, mTestListener); + mReader = new InputReader(std::make_shared(), mFakePolicy, mTestListener); ASSERT_EQ(mReader->start(), OK); // Since this test is run on a real device, all the input devices connected -- cgit v1.2.3-59-g8ed1b From fbe732ebbb25870086610e9a32acdb37df402612 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Fri, 24 Jan 2020 11:26:14 -0800 Subject: Add ID to native events. To separate this big work into pieces I introduced a placeholder in InputTransport used to initialize native input events received from InputFlinger until InputFlinger can generate random sequence numbers. The work in InputDispatcher wires up ID between events and notify args as well. Bug: 144889238 Test: Builds and some smoke tests. Test: atest libinput_tests Change-Id: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce Merged-In: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce (cherry picked from commit 4cc839fe01cd5bbb16650e85913070386f637e75) --- include/input/Input.h | 14 ++-- libs/input/Input.cpp | 26 +++++--- libs/input/InputTransport.cpp | 37 ++++++----- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 37 ++++++----- libs/input/tests/VelocityTracker_test.cpp | 11 ++-- libs/input/tests/VerifiedInputEvent_test.cpp | 11 ++-- .../benchmarks/InputDispatcher_benchmarks.cpp | 5 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 55 ++++++++-------- .../inputflinger/tests/InputDispatcher_test.cpp | 76 ++++++++++++---------- 10 files changed, 160 insertions(+), 118 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index b86d761e48..9e47318203 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -397,6 +397,8 @@ public: virtual int32_t getType() const = 0; + inline int32_t getId() const { return mId; } + inline int32_t getDeviceId() const { return mDeviceId; } inline uint32_t getSource() const { return mSource; } @@ -409,11 +411,15 @@ public: inline std::array getHmac() const { return mHmac; } + static int32_t nextId(); + protected: - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac); + void initialize(const InputEvent& from); + int32_t mId; int32_t mDeviceId; uint32_t mSource; int32_t mDisplayId; @@ -450,7 +456,7 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -674,7 +680,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -754,7 +760,7 @@ public: inline bool getInTouchMode() const { return mInTouchMode; } - void initialize(bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus, bool inTouchMode); void initialize(const FocusEvent& from); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index abd031ae26..c2437673df 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -110,8 +110,9 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) event.getButtonState()}; } -void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { + mId = id; mDeviceId = deviceId; mSource = source; mDisplayId = displayId; @@ -119,12 +120,18 @@ void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId } void InputEvent::initialize(const InputEvent& from) { + mId = from.mId; mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; mHmac = from.mHmac; } +int32_t InputEvent::nextId() { + static IdGenerator idGen(IdGenerator::Source::OTHER); + return idGen.nextId(); +} + // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { @@ -135,11 +142,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -298,7 +305,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, @@ -307,7 +314,7 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -332,7 +339,8 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); + InputEvent::initialize(other->mId, other->mDeviceId, other->mSource, other->mDisplayId, + other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -540,6 +548,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { return BAD_VALUE; } + mId = parcel->readInt32(); mDeviceId = parcel->readInt32(); mSource = parcel->readUint32(); mDisplayId = parcel->readInt32(); @@ -601,6 +610,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(pointerCount); parcel->writeInt32(sampleCount); + parcel->writeInt32(mId); parcel->writeInt32(mDeviceId); parcel->writeUint32(mSource); parcel->writeInt32(mDisplayId); @@ -670,8 +680,8 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { // --- FocusEvent --- -void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { - InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, +void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d25a5cc0ef..eda01c5032 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -63,6 +63,10 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; +// A placeholder sequence number used to initialize native input events before InputFlinger is +// migrated to new sequence number system. +static constexpr int32_t INPUT_FLINGER_SEQUENCE_NUM = 0; + /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better @@ -1142,14 +1146,16 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, - msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, - msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, - msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, + msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, + msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, + msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.focus.hasFocus == 1, + msg->body.focus.inTouchMode == 1); } void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { @@ -1161,16 +1167,17 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, - msg->body.motion.actionButton, msg->body.motion.flags, - msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, - msg->body.motion.yOffset, msg->body.motion.xPrecision, - msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, - msg->body.motion.yCursorPosition, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.motion.deviceId, + msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, + msg->body.motion.action, msg->body.motion.actionButton, + msg->body.motion.flags, msg->body.motion.edgeFlags, + msg->body.motion.metaState, msg->body.motion.buttonState, + msg->body.motion.classification, msg->body.motion.xScale, + msg->body.motion.yScale, msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 6f9b162986..cb68165433 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, - 0, time, time); + event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, + 0, metaState, 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index d0f761887a..553dc4c068 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -182,10 +182,12 @@ TEST_F(KeyEventTest, Properties) { // Initialize and get properties. constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + const int32_t id = InputEvent::nextId(); + event.initialize(id, 2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + ASSERT_EQ(id, event.getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); @@ -222,12 +224,16 @@ protected: static constexpr float X_OFFSET = 1; static constexpr float Y_OFFSET = 1.1; + int32_t mId; + void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + mId = InputEvent::nextId(); + PointerProperties pointerProperties[2]; pointerProperties[0].clear(); pointerProperties[0].id = 1; @@ -257,10 +263,10 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, - AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -308,6 +314,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. + ASSERT_EQ(mId, event->getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); @@ -577,8 +584,8 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, @@ -642,10 +649,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/, + 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -663,10 +670,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, + 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 731eb6a000..bf452c07a3 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,12 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, - 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, - 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index a59dbe5987..4e8e840d1c 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -21,9 +21,10 @@ namespace android { static KeyEvent getKeyEventWithFlags(int32_t flags) { KeyEvent event; - event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/, - AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/); + event.initialize(InputEvent::nextId(), 2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, + ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags, + AKEYCODE_BUTTON_X, 121 /*scanCode*/, AMETA_ALT_ON, 1 /*repeatCount*/, + 1000 /*downTime*/, 2000 /*eventTime*/); return event; } @@ -38,8 +39,8 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, + INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9a6ef21724..238cc39646 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,8 +203,9 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f9a86dd6bb..70cee18330 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3030,9 +3030,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, - flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, - args->eventTime); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, flags, keyCode, args->scanCode, metaState, repeatCount, + args->downTime, args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3125,13 +3125,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, - args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, 1 /*xScale*/, - 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->eventTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, + INVALID_HMAC, args->action, args->actionButton, args->flags, + args->edgeFlags, args->metaState, args->buttonState, + args->classification, 1 /*xScale*/, 1 /*yScale*/, 0 /* xOffset */, + 0 /* yOffset */, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, args->downTime, + args->eventTime, args->pointerCount, args->pointerProperties, + args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3227,7 +3228,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action, /*byref*/ keyCode, /*byref*/ metaState); KeyEvent keyEvent; - keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode, incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime(), incomingKey.getEventTime()); @@ -3247,11 +3248,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); KeyEntry* injectedEntry = - new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, keyEvent.getKeyCode(), - keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); + new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(), + VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + incomingKey.getDisplayId(), policyFlags, action, flags, + incomingKey.getKeyCode(), incomingKey.getScanCode(), + incomingKey.getMetaState(), incomingKey.getRepeatCount(), + incomingKey.getDownTime()); injectedEntries.push(injectedEntry); break; } @@ -3281,13 +3283,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); MotionEntry* injectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, actionButton, - motionEvent->getFlags(), motionEvent->getMetaState(), - motionEvent->getButtonState(), motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, + motionEvent->getSource(), motionEvent->getDisplayId(), + policyFlags, action, actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getClassification(), motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), uint32_t(pointerCount), @@ -3298,7 +3299,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec sampleEventTimes += 1; samplePointerCoords += pointerCount; MotionEntry* nextInjectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), @@ -4817,9 +4818,9 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, - entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, - entry.downTime, entry.eventTime); + event.initialize(entry.sequenceNum, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, + entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, + entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2fb1b65d93..f05f7e5a79 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -49,9 +49,9 @@ struct PointF { static KeyEvent getTestKeyEvent() { KeyEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, - ARBITRARY_TIME); + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + ARBITRARY_TIME, ARBITRARY_TIME); return event; } @@ -300,7 +300,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( @@ -309,8 +310,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { << "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, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, 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, @@ -335,7 +336,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, @@ -346,7 +347,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -358,7 +359,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -371,7 +372,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -383,7 +384,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -396,20 +397,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -418,10 +421,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -429,10 +433,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -442,10 +447,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -748,8 +754,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, + AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. @@ -777,7 +784,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, -- cgit v1.2.3-59-g8ed1b From c51d1ba0d7af0fbc33b8e8dc6e54f6bd7f534267 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Tue, 28 Jan 2020 13:24:04 -0800 Subject: Rename sequenceNum to ID. This is for consistency with ID in other places. Change it to signed type to match Java int type. Bug: 144889238 Test: atest inputflinger_tests Change-Id: I8b6782a67f28aa13f7feaed2c97b8e87105019db Merged-In: I8b6782a67f28aa13f7feaed2c97b8e87105019db (cherry picked from commit 6a5a14e75b1a0fb377c670cb9a1e9ace60c5da74) --- services/inputflinger/InputListener.cpp | 146 ++++++++++----------- .../benchmarks/InputDispatcher_benchmarks.cpp | 6 +- services/inputflinger/dispatcher/Entry.cpp | 25 ++-- services/inputflinger/dispatcher/Entry.h | 37 +++--- .../inputflinger/dispatcher/InputDispatcher.cpp | 66 +++++----- services/inputflinger/dispatcher/InputState.cpp | 28 ++-- services/inputflinger/include/InputListener.h | 23 ++-- services/inputflinger/reader/InputDevice.cpp | 2 +- services/inputflinger/reader/InputReader.cpp | 8 +- services/inputflinger/reader/include/InputReader.h | 6 +- .../reader/include/InputReaderContext.h | 2 +- .../reader/mapper/CursorInputMapper.cpp | 34 +++-- .../reader/mapper/JoystickInputMapper.cpp | 7 +- .../reader/mapper/KeyboardInputMapper.cpp | 5 +- .../reader/mapper/RotaryEncoderInputMapper.cpp | 6 +- .../reader/mapper/SwitchInputMapper.cpp | 2 +- .../reader/mapper/TouchCursorInputMapperCommon.h | 4 +- .../reader/mapper/TouchInputMapper.cpp | 50 +++---- .../inputflinger/tests/InputDispatcher_test.cpp | 25 ++-- services/inputflinger/tests/InputReader_test.cpp | 32 +++-- 20 files changed, 246 insertions(+), 268 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index de639772a8..e91e803fb8 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -27,18 +27,15 @@ namespace android { // --- NotifyConfigurationChangedArgs --- -NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs( - uint32_t sequenceNum, nsecs_t eventTime) : - NotifyArgs(sequenceNum, eventTime) { -} +NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime) + : NotifyArgs(id, eventTime) {} NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs( - const NotifyConfigurationChangedArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime) { -} + const NotifyConfigurationChangedArgs& other) + : NotifyArgs(other.id, other.eventTime) {} bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const { - return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime; + return id == rhs.id && eventTime == rhs.eventTime; } void NotifyConfigurationChangedArgs::notify(const sp& listener) const { @@ -48,37 +45,39 @@ void NotifyConfigurationChangedArgs::notify(const sp& li // --- NotifyKeyArgs --- -NotifyKeyArgs::NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, - int32_t metaState, nsecs_t downTime) : - NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source), - displayId(displayId), policyFlags(policyFlags), - action(action), flags(flags), keyCode(keyCode), scanCode(scanCode), - metaState(metaState), downTime(downTime) { -} +NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) + : NotifyArgs(id, eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + flags(flags), + keyCode(keyCode), + scanCode(scanCode), + metaState(metaState), + downTime(downTime) {} -NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId), - source(other.source), displayId(other.displayId), policyFlags(other.policyFlags), - action(other.action), flags(other.flags), - keyCode(other.keyCode), scanCode(other.scanCode), - metaState(other.metaState), downTime(other.downTime) { -} +NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) + : NotifyArgs(other.id, other.eventTime), + deviceId(other.deviceId), + source(other.source), + displayId(other.displayId), + policyFlags(other.policyFlags), + action(other.action), + flags(other.flags), + keyCode(other.keyCode), + scanCode(other.scanCode), + metaState(other.metaState), + downTime(other.downTime) {} bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const { - return sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && deviceId == rhs.deviceId - && source == rhs.source - && displayId == rhs.displayId - && policyFlags == rhs.policyFlags - && action == rhs.action - && flags == rhs.flags - && keyCode == rhs.keyCode - && scanCode == rhs.scanCode - && metaState == rhs.metaState - && downTime == rhs.downTime; + return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId && + source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags && + action == rhs.action && flags == rhs.flags && keyCode == rhs.keyCode && + scanCode == rhs.scanCode && metaState == rhs.metaState && downTime == rhs.downTime; } void NotifyKeyArgs::notify(const sp& listener) const { @@ -88,15 +87,17 @@ void NotifyKeyArgs::notify(const sp& listener) const { // --- NotifyMotionArgs --- -NotifyMotionArgs::NotifyMotionArgs( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - const std::vector& videoFrames) - : NotifyArgs(sequenceNum, eventTime), +NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, + const std::vector& videoFrames) + : NotifyArgs(id, eventTime), deviceId(deviceId), source(source), displayId(displayId), @@ -122,7 +123,7 @@ NotifyMotionArgs::NotifyMotionArgs( } NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) - : NotifyArgs(other.sequenceNum, other.eventTime), + : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId), source(other.source), displayId(other.displayId), @@ -152,12 +153,11 @@ static inline bool isCursorPositionEqual(float lhs, float rhs) { } bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { - bool equal = sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime && - deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && - policyFlags == rhs.policyFlags && action == rhs.action && - actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && - buttonState == rhs.buttonState && classification == rhs.classification && - edgeFlags == rhs.edgeFlags && + bool equal = id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId && + source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags && + action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags && + metaState == rhs.metaState && buttonState == rhs.buttonState && + classification == rhs.classification && edgeFlags == rhs.edgeFlags && pointerCount == rhs.pointerCount // PointerProperties and PointerCoords are compared separately below && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && @@ -186,23 +186,22 @@ void NotifyMotionArgs::notify(const sp& listener) const // --- NotifySwitchArgs --- -NotifySwitchArgs::NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags, - uint32_t switchValues, uint32_t switchMask) : - NotifyArgs(sequenceNum, eventTime), policyFlags(policyFlags), - switchValues(switchValues), switchMask(switchMask) { -} +NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, + uint32_t switchValues, uint32_t switchMask) + : NotifyArgs(id, eventTime), + policyFlags(policyFlags), + switchValues(switchValues), + switchMask(switchMask) {} -NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), policyFlags(other.policyFlags), - switchValues(other.switchValues), switchMask(other.switchMask) { -} +NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) + : NotifyArgs(other.id, other.eventTime), + policyFlags(other.policyFlags), + switchValues(other.switchValues), + switchMask(other.switchMask) {} bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const { - return sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && policyFlags == rhs.policyFlags - && switchValues == rhs.switchValues - && switchMask == rhs.switchMask; + return id == rhs.id && eventTime == rhs.eventTime && policyFlags == rhs.policyFlags && + switchValues == rhs.switchValues && switchMask == rhs.switchMask; } void NotifySwitchArgs::notify(const sp& listener) const { @@ -212,19 +211,14 @@ void NotifySwitchArgs::notify(const sp& listener) const // --- NotifyDeviceResetArgs --- -NotifyDeviceResetArgs::NotifyDeviceResetArgs( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) : - NotifyArgs(sequenceNum, eventTime), deviceId(deviceId) { -} +NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId) + : NotifyArgs(id, eventTime), deviceId(deviceId) {} -NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId) { -} +NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) + : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId) {} bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const { - return sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && deviceId == rhs.deviceId; + return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId; } void NotifyDeviceResetArgs::notify(const sp& listener) const { diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 238cc39646..3b18813cef 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -229,7 +229,7 @@ static NotifyMotionArgs generateMotionArgs() { const nsecs_t currentTime = now(); // Define a valid motion event. - NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, @@ -259,14 +259,14 @@ static void benchmarkNotifyMotion(benchmark::State& state) { for (auto _ : state) { // Send ACTION_DOWN motionArgs.action = AMOTION_EVENT_ACTION_DOWN; - motionArgs.sequenceNum = 0; + motionArgs.id = 0; motionArgs.downTime = now(); motionArgs.eventTime = motionArgs.downTime; dispatcher->notifyMotion(&motionArgs); // Send ACTION_UP motionArgs.action = AMOTION_EVENT_ACTION_UP; - motionArgs.sequenceNum = 1; + motionArgs.id = 1; motionArgs.eventTime = now(); dispatcher->notifyMotion(&motionArgs); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index c4b3789090..49630ad436 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -86,8 +86,8 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) // --- EventEntry --- -EventEntry::EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags) - : sequenceNum(sequenceNum), +EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags) + : id(id), refCount(1), type(type), eventTime(eventTime), @@ -117,8 +117,8 @@ void EventEntry::releaseInjectionState() { // --- ConfigurationChangedEntry --- -ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime) - : EventEntry(sequenceNum, Type::CONFIGURATION_CHANGED, eventTime, 0) {} +ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime) + : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {} ConfigurationChangedEntry::~ConfigurationChangedEntry() {} @@ -128,8 +128,8 @@ void ConfigurationChangedEntry::appendDescription(std::string& msg) const { // --- DeviceResetEntry --- -DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) - : EventEntry(sequenceNum, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} +DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId) + : EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} DeviceResetEntry::~DeviceResetEntry() {} @@ -140,9 +140,8 @@ void DeviceResetEntry::appendDescription(std::string& msg) const { // --- FocusEntry --- // Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries -FocusEntry::FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp connectionToken, - bool hasFocus) - : EventEntry(sequenceNum, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER), +FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp connectionToken, bool hasFocus) + : EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER), connectionToken(connectionToken), hasFocus(hasFocus) {} @@ -154,11 +153,11 @@ void FocusEntry::appendDescription(std::string& msg) const { // --- KeyEntry --- -KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, +KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime) - : EventEntry(sequenceNum, Type::KEY, eventTime, policyFlags), + : EventEntry(id, Type::KEY, eventTime, policyFlags), deviceId(deviceId), source(source), displayId(displayId), @@ -198,7 +197,7 @@ void KeyEntry::recycle() { // --- MotionEntry --- -MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, +MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, @@ -206,7 +205,7 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset) - : EventEntry(sequenceNum, Type::MOTION, eventTime, policyFlags), + : EventEntry(id, Type::MOTION, eventTime, policyFlags), eventTime(eventTime), deviceId(deviceId), source(source), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index b5b61ccb98..c58ae23cc5 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -30,7 +30,7 @@ namespace android::inputdispatcher { // Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; +constexpr int32_t SYNTHESIZED_EVENT_ID = 0; struct EventEntry { enum class Type { @@ -56,7 +56,7 @@ struct EventEntry { } } - uint32_t sequenceNum; + int32_t id; mutable int32_t refCount; Type type; nsecs_t eventTime; @@ -78,22 +78,20 @@ struct EventEntry { * Key repeat is a synthesized event, because it is related to an actual hardware state * (a key is currently pressed), but the repeat itself is generated by the framework. */ - inline bool isSynthesized() const { - return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM; - } + inline bool isSynthesized() const { return isInjected() || id == SYNTHESIZED_EVENT_ID; } void release(); virtual void appendDescription(std::string& msg) const = 0; protected: - EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags); + EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); void releaseInjectionState(); }; struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); + explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime); virtual void appendDescription(std::string& msg) const; protected: @@ -103,7 +101,7 @@ protected: struct DeviceResetEntry : EventEntry { int32_t deviceId; - DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); + DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId); virtual void appendDescription(std::string& msg) const; protected: @@ -114,7 +112,7 @@ struct FocusEntry : EventEntry { sp connectionToken; bool hasFocus; - FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp connectionToken, bool hasFocus); + FocusEntry(int32_t id, nsecs_t eventTime, sp connectionToken, bool hasFocus); virtual void appendDescription(std::string& msg) const; protected: @@ -144,10 +142,9 @@ struct KeyEntry : EventEntry { InterceptKeyResult interceptKeyResult; // set based on the interception result nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER - KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime); + KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, int32_t repeatCount, nsecs_t downTime); virtual void appendDescription(std::string& msg) const; void recycle(); @@ -176,13 +173,13 @@ struct MotionEntry : EventEntry { PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, - uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xOffset, float yOffset); + MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); virtual void appendDescription(std::string& msg) const; protected: diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 70cee18330..2f7b5ad9e6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -302,7 +302,7 @@ static std::unique_ptr createDispatchEntry(const InputTarget& inp } MotionEntry* combinedMotionEntry = - new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, + new MotionEntry(motionEntry.id, motionEntry.eventTime, motionEntry.deviceId, motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, motionEntry.action, motionEntry.actionButton, motionEntry.flags, motionEntry.metaState, motionEntry.buttonState, @@ -930,9 +930,9 @@ KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { entry->repeatCount += 1; } else { KeyEntry* newEntry = - new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId, - entry->source, entry->displayId, policyFlags, entry->action, - entry->flags, entry->keyCode, entry->scanCode, entry->metaState, + new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, entry->deviceId, entry->source, + entry->displayId, policyFlags, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount + 1, entry->downTime); mKeyRepeatState.lastKeyEntry = newEntry; @@ -981,7 +981,7 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { FocusEntry* focusEntry = - new FocusEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, now(), window.getToken(), hasFocus); + new FocusEntry(SYNTHESIZED_EVENT_ID, now(), window.getToken(), hasFocus); enqueueInboundEventLocked(focusEntry); } @@ -1076,7 +1076,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, setInjectionResult(entry, *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - mReporter->reportDroppedKey(entry->sequenceNum); + mReporter->reportDroppedKey(entry->id); return true; } @@ -2188,8 +2188,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=%" PRIx32 ")", + connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } #if DEBUG_DISPATCH_CYCLE @@ -2244,9 +2244,8 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 - ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=%" PRIx32 ")", + connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } @@ -2922,7 +2921,7 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotion } MotionEntry* splitMotionEntry = - new MotionEntry(originalMotionEntry.sequenceNum, originalMotionEntry.eventTime, + new MotionEntry(originalMotionEntry.id, originalMotionEntry.eventTime, originalMotionEntry.deviceId, originalMotionEntry.source, originalMotionEntry.displayId, originalMotionEntry.policyFlags, action, originalMotionEntry.actionButton, originalMotionEntry.flags, @@ -2951,7 +2950,7 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange std::scoped_lock _l(mLock); ConfigurationChangedEntry* newEntry = - new ConfigurationChangedEntry(args->sequenceNum, args->eventTime); + new ConfigurationChangedEntry(args->id, args->eventTime); needWake = enqueueInboundEventLocked(newEntry); } // release lock @@ -3030,7 +3029,7 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, INVALID_HMAC, + event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); @@ -3057,7 +3056,7 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { } KeyEntry* newEntry = - new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + new KeyEntry(args->id, args->eventTime, args->deviceId, args->source, args->displayId, policyFlags, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime); @@ -3076,15 +3075,15 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " + ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", policyFlags=0x%x, " "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " "yCursorPosition=%f, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, args->xCursorPosition, - args->yCursorPosition, args->downTime); + args->id, args->eventTime, args->deviceId, args->source, args->displayId, + args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, + args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -3125,14 +3124,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, - INVALID_HMAC, args->action, args->actionButton, args->flags, - args->edgeFlags, args->metaState, args->buttonState, - args->classification, 1 /*xScale*/, 1 /*yScale*/, 0 /* xOffset */, - 0 /* yOffset */, args->xPrecision, args->yPrecision, - args->xCursorPosition, args->yCursorPosition, args->downTime, - args->eventTime, args->pointerCount, args->pointerProperties, - args->pointerCoords); + event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, args->actionButton, args->flags, args->edgeFlags, + args->metaState, args->buttonState, args->classification, 1 /*xScale*/, + 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3144,7 +3142,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { // Just enqueue a new motion event. MotionEntry* newEntry = - new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + new MotionEntry(args->id, args->eventTime, args->deviceId, args->source, args->displayId, policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->classification, args->edgeFlags, args->xPrecision, @@ -3188,7 +3186,7 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { std::scoped_lock _l(mLock); DeviceResetEntry* newEntry = - new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId); + new DeviceResetEntry(args->id, args->eventTime, args->deviceId); needWake = enqueueInboundEventLocked(newEntry); } // release lock @@ -4631,7 +4629,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) { if (!handled) { // Report the key as unhandled, since the fallback was not handled. - mReporter->reportUnhandledKey(keyEntry->sequenceNum); + mReporter->reportUnhandledKey(keyEntry->id); } return false; } @@ -4796,7 +4794,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con #endif // Report the key as unhandled, since there is no fallback key. - mReporter->reportUnhandledKey(keyEntry->sequenceNum); + mReporter->reportUnhandledKey(keyEntry->id); } } return false; @@ -4818,7 +4816,7 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.sequenceNum, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, + event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime, entry.eventTime); return event; diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 053598ab02..7fa9e09aad 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -268,9 +268,9 @@ std::vector InputState::synthesizeCancelationEvents( std::vector events; for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { - events.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, - memento.policyFlags, AKEY_EVENT_ACTION_UP, + events.push_back(new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, + AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, memento.scanCode, memento.metaState, 0 /*repeatCount*/, memento.downTime)); @@ -281,11 +281,10 @@ std::vector InputState::synthesizeCancelationEvents( if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, - memento.policyFlags, action, 0 /*actionButton*/, - memento.flags, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, + events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, + action, 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, @@ -332,16 +331,15 @@ std::vector InputState::synthesizePointerDownEvents(nsecs_t current : AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, - memento.policyFlags, action, 0 /*actionButton*/, - memento.flags, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, + events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, + action, 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, - pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/)); + pointerCount, pointerProperties, pointerCoords, + 0 /*xOffset*/, 0 /*yOffset*/)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index 0dcd2f9c38..f8d0150e87 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -31,13 +31,12 @@ class InputListenerInterface; /* Superclass of all input event argument objects */ struct NotifyArgs { - uint32_t sequenceNum; + int32_t id; nsecs_t eventTime; - inline NotifyArgs() : sequenceNum(0), eventTime(0) { } + inline NotifyArgs() : id(0), eventTime(0) {} - inline explicit NotifyArgs(uint32_t sequenceNum, nsecs_t eventTime) : - sequenceNum(sequenceNum), eventTime(eventTime) { } + inline explicit NotifyArgs(int32_t id, nsecs_t eventTime) : id(id), eventTime(eventTime) {} virtual ~NotifyArgs() { } @@ -52,7 +51,7 @@ struct NotifyConfigurationChangedArgs : public NotifyArgs { bool operator==(const NotifyConfigurationChangedArgs& rhs) const; - NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime); + NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime); NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other); @@ -77,9 +76,9 @@ struct NotifyKeyArgs : public NotifyArgs { inline NotifyKeyArgs() { } - NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime); + NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime); bool operator==(const NotifyKeyArgs& rhs) const; @@ -125,7 +124,7 @@ struct NotifyMotionArgs : public NotifyArgs { inline NotifyMotionArgs() { } - NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount, @@ -152,8 +151,8 @@ struct NotifySwitchArgs : public NotifyArgs { inline NotifySwitchArgs() { } - NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags, - uint32_t switchValues, uint32_t switchMask); + NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues, + uint32_t switchMask); NotifySwitchArgs(const NotifySwitchArgs& other); @@ -172,7 +171,7 @@ struct NotifyDeviceResetArgs : public NotifyArgs { inline NotifyDeviceResetArgs() { } - NotifyDeviceResetArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); + NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId); NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other); diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index f2afc818ff..4b19e5e353 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -454,7 +454,7 @@ void InputDevice::bumpGeneration() { } void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); + NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); mContext->getListener()->notifyDeviceReset(&args); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 6ab5902bb7..2998cc9619 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -46,7 +46,7 @@ InputReader::InputReader(std::shared_ptr eventHub, : mContext(this), mEventHub(eventHub), mPolicy(policy), - mNextSequenceNum(1), + mNextId(1), mGlobalMetaState(0), mGeneration(1), mNextInputDeviceId(END_RESERVED_ID), @@ -313,7 +313,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { updateGlobalMetaStateLocked(); // Enqueue configuration changed. - NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); + NotifyConfigurationChangedArgs args(mContext.getNextId(), when); mQueuedListener->notifyConfigurationChanged(&args); } @@ -761,8 +761,8 @@ EventHubInterface* InputReader::ContextImpl::getEventHub() { return mReader->mEventHub.get(); } -uint32_t InputReader::ContextImpl::getNextSequenceNum() { - return (mReader->mNextSequenceNum)++; +int32_t InputReader::ContextImpl::getNextId() { + return (mReader->mNextId)++; } } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index ca1a081e48..eaa105a8f6 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -111,7 +111,7 @@ protected: virtual InputReaderPolicyInterface* getPolicy() override; virtual InputListenerInterface* getListener() override; virtual EventHubInterface* getEventHub() override; - virtual uint32_t getNextSequenceNum() override; + virtual int32_t getNextId() override; } mContext; friend class ContextImpl; @@ -132,8 +132,8 @@ private: InputReaderConfiguration mConfig; - // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers - uint32_t mNextSequenceNum; + // used by InputReaderContext::getNextId() as a counter for event sequence numbers + uint32_t mNextId; // The event queue. static const int EVENT_BUFFER_SIZE = 256; diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index d5527cf926..85701e4f63 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -58,7 +58,7 @@ public: virtual InputListenerInterface* getListener() = 0; virtual EventHubInterface* getEventHub() = 0; - virtual uint32_t getNextSequenceNum() = 0; + virtual int32_t getNextId() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index c4162bc510..887ab53c76 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -168,7 +168,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } bumpGeneration(); if (changes) { - NotifyDeviceResetArgs args(getContext()->getNextSequenceNum(), when, getDeviceId()); + NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); getListener()->notifyDeviceReset(&args); } } @@ -374,8 +374,8 @@ void CursorInputMapper::sync(nsecs_t when) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(getContext()->getNextSequenceNum(), when, - getDeviceId(), mSource, displayId, policyFlags, + NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -386,11 +386,11 @@ void CursorInputMapper::sync(nsecs_t when) { } } - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); @@ -399,8 +399,8 @@ void CursorInputMapper::sync(nsecs_t when) { while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - NotifyMotionArgs pressArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, + NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -415,10 +415,9 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, + NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); @@ -430,10 +429,9 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs scrollArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, + NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 7c3b1d9b40..030a846727 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -333,10 +333,9 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, + ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index ab354a2e2b..7be4a58cc3 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -348,9 +348,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - NotifyKeyArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - getDisplayId(), policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); getListener()->notifyKey(&args); } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index ed93f14cea..98858897f4 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -119,9 +119,9 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = getContext()->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(getContext()->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, - 0, metaState, /* buttonState */ 0, MotionClassification::NONE, + NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index e79aeb28c4..4f736810bc 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -56,7 +56,7 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { void SwitchInputMapper::sync(nsecs_t when) { if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(getContext()->getNextSequenceNum(), when, 0 /*policyFlags*/, + NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, updatedSwitchValues, mUpdatedSwitchMask); getListener()->notifySwitch(&args); diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index efa3d6d2b2..2a3e2637f1 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -65,8 +65,8 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nse (currentButtonState & buttonState)) || (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); + NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags, + action, 0, keyCode, 0, context->getGlobalMetaState(), when); context->getListener()->notifyKey(&args); } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 7543374ebd..bbc8e537c6 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -386,7 +386,7 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c if (changes && resetNeeded) { // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - NotifyDeviceResetArgs args(getContext()->getNextSequenceNum(), when, getDeviceId()); + NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); getListener()->notifyDeviceReset(&args); } } @@ -1824,9 +1824,9 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - NotifyKeyArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, - keyEventFlags, keyCode, scanCode, metaState, downTime); + NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); getListener()->notifyKey(&args); } @@ -2498,11 +2498,11 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, - 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerProperties, &pointerCoords, 0, 0, x, y, + mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3418,8 +3418,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, @@ -3432,9 +3432,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, - metaState, mLastRawState.buttonState, MotionClassification::NONE, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, @@ -3448,7 +3448,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, @@ -3459,8 +3459,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send move. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, @@ -3474,7 +3474,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, @@ -3485,9 +3485,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send hover move. - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, @@ -3507,8 +3507,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, @@ -3581,8 +3581,8 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 std::vector frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(getContext()->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, + NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags, + action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, std::move(frames)); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f05f7e5a79..270f891d36 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -89,7 +89,7 @@ public: void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { ASSERT_TRUE(mLastNotifySwitch); - // We do not check sequenceNum because it is not exposed to the policy + // We do not check id because it is not exposed to the policy EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime); EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags); EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues); @@ -171,8 +171,7 @@ private: /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is * essentially a passthrough for notifySwitch. */ - mLastNotifySwitch = - NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask); + mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask); } virtual void pokeUserActivity(nsecs_t, int32_t) { @@ -463,7 +462,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { constexpr nsecs_t eventTime = 20; - NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime); + NotifyConfigurationChangedArgs args(10 /*id*/, eventTime); mDispatcher->notifyConfigurationChanged(&args); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -471,8 +470,8 @@ TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { } TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { - NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/, - 1 /*switchValues*/, 2 /*switchMask*/); + NotifySwitchArgs args(10 /*id*/, 20 /*eventTime*/, 0 /*policyFlags*/, 1 /*switchValues*/, + 2 /*switchMask*/); mDispatcher->notifySwitch(&args); // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener @@ -813,9 +812,9 @@ static int32_t injectMotionUp(const sp& dispatcher, int32_t sou static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, - AKEYCODE_A, KEY_A, AMETA_NONE, currentTime); + NotifyKeyArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A, KEY_A, + AMETA_NONE, currentTime); return args; } @@ -842,7 +841,7 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. - NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, + NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, source, displayId, POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties, @@ -1005,7 +1004,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { // When device reset happens, that key stream should be terminated with FLAG_CANCELED // on the app side. - NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID); + NotifyDeviceResetArgs args(10 /*id*/, 20 /*eventTime*/, DEVICE_ID); mDispatcher->notifyDeviceReset(&args); window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED); @@ -1028,7 +1027,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { // When device reset happens, that motion stream should be terminated with ACTION_CANCEL // on the app side. - NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID); + NotifyDeviceResetArgs args(10 /*id*/, 20 /*eventTime*/, DEVICE_ID); mDispatcher->notifyDeviceReset(&args); window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT, 0 /*expectedFlags*/); @@ -1353,7 +1352,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { window->consumeMotionDown(ADISPLAY_ID_DEFAULT); motionArgs.action = AMOTION_EVENT_ACTION_MOVE; - motionArgs.sequenceNum += 1; + motionArgs.id += 1; motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC); motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, motionArgs.pointerCoords[0].getX() - 10); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 4da9d93890..46cd4809fc 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -846,7 +846,7 @@ class FakeInputReaderContext : public InputReaderContext { int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; int32_t mGeneration; - uint32_t mNextSequenceNum; + int32_t mNextId; wp mPointerController; public: @@ -857,7 +857,7 @@ public: mPolicy(policy), mListener(listener), mGlobalMetaState(0), - mNextSequenceNum(1) {} + mNextId(1) {} virtual ~FakeInputReaderContext() { } @@ -941,9 +941,7 @@ private: } - virtual uint32_t getNextSequenceNum() { - return mNextSequenceNum++; - } + virtual int32_t getNextId() { return mNextId++; } }; @@ -1644,7 +1642,7 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { ASSERT_EQ(1, event.value); } -TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { +TEST_F(InputReaderTest, DeviceReset_IncrementsId) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; constexpr int32_t eventHubId = 1; @@ -1656,25 +1654,25 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) { NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - uint32_t prevSequenceNum = resetArgs.sequenceNum; + int32_t prevId = resetArgs.id; disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); - prevSequenceNum = resetArgs.sequenceNum; + ASSERT_TRUE(prevId < resetArgs.id); + prevId = resetArgs.id; enableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); - prevSequenceNum = resetArgs.sequenceNum; + ASSERT_TRUE(prevId < resetArgs.id); + prevId = resetArgs.id; disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum); - prevSequenceNum = resetArgs.sequenceNum; + ASSERT_TRUE(prevId < resetArgs.id); + prevId = resetArgs.id; } TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { @@ -1816,21 +1814,21 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { NotifyConfigurationChangedArgs configChangedArgs; ASSERT_NO_FATAL_FAILURE( mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs)); - uint32_t prevSequenceNum = configChangedArgs.sequenceNum; + int32_t prevId = configChangedArgs.id; nsecs_t prevTimestamp = configChangedArgs.eventTime; NotifyKeyArgs keyArgs; keyboard->pressAndReleaseHomeKey(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum); - prevSequenceNum = keyArgs.sequenceNum; + ASSERT_LT(prevId, keyArgs.id); + prevId = keyArgs.id; ASSERT_LE(prevTimestamp, keyArgs.eventTime); prevTimestamp = keyArgs.eventTime; ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum); + ASSERT_LT(prevId, keyArgs.id); ASSERT_LE(prevTimestamp, keyArgs.eventTime); } -- cgit v1.2.3-59-g8ed1b From 1c7bc86a9b1bdf16b240a96d083102127f036325 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Tue, 28 Jan 2020 13:24:04 -0800 Subject: Let InputFlinger generate event IDs. Also send event IDs via InputMessage and add some atrace calls to form a complete chain of input event processing. Bug: 144889238 Test: systrace shows correct event IDs. Test: atest inputflinger_tests Change-Id: I3c561b03b0ba75c22115ae020e6b41855686ab64 Merged-In: I3c561b03b0ba75c22115ae020e6b41855686ab64 (cherry picked from commit ff1f1bb99489fd372c57908dafdd3817a33db0c5) --- include/input/InputTransport.h | 31 +++--- libs/input/InputTransport.cpp | 53 +++++----- .../input/tests/InputPublisherAndConsumer_test.cpp | 26 +++-- libs/input/tests/StructLayout_test.cpp | 9 +- services/inputflinger/Android.bp | 1 + services/inputflinger/InputListener.cpp | 18 ++++ services/inputflinger/dispatcher/Connection.cpp | 4 +- services/inputflinger/dispatcher/Connection.h | 2 +- services/inputflinger/dispatcher/Entry.h | 10 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 72 ++++++++++---- services/inputflinger/dispatcher/InputDispatcher.h | 2 + services/inputflinger/dispatcher/InputState.cpp | 10 +- services/inputflinger/dispatcher/InputState.h | 4 +- services/inputflinger/reader/InputReader.cpp | 6 +- services/inputflinger/reader/include/InputReader.h | 4 +- .../inputflinger/tests/InputDispatcher_test.cpp | 109 +++++++++++++++++++++ services/inputflinger/tests/InputReader_test.cpp | 27 +++-- 17 files changed, 293 insertions(+), 95 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 06fd3bb364..8ca178c1d7 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -82,7 +82,7 @@ struct InputMessage { union Body { struct Key { uint32_t seq; - uint32_t empty1; + int32_t eventId; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -102,7 +102,7 @@ struct InputMessage { struct Motion { uint32_t seq; - uint32_t empty1; + int32_t eventId; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -159,6 +159,8 @@ struct InputMessage { struct Focus { uint32_t seq; + int32_t eventId; + uint32_t empty1; // The following two fields take up 4 bytes total uint16_t hasFocus; // actually a bool uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment @@ -276,9 +278,9 @@ public: * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - std::array hmac, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, + status_t publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. @@ -289,14 +291,15 @@ public: * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ - status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - std::array hmac, int32_t action, int32_t actionButton, - int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, - float xScale, float yScale, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, - uint32_t pointerCount, const PointerProperties* pointerProperties, + status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, + float xOffset, float yOffset, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Publishes a focus event to the input channel. @@ -306,7 +309,7 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode); + status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index eda01c5032..7335b30a49 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -63,10 +63,6 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; -// A placeholder sequence number used to initialize native input events before InputFlinger is -// migrated to new sequence number system. -static constexpr int32_t INPUT_FLINGER_SEQUENCE_NUM = 0; - /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better @@ -143,6 +139,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::KEY: { // uint32_t seq msg->body.key.seq = body.key.seq; + // int32_t eventId + msg->body.key.eventId = body.key.eventId; // nsecs_t eventTime msg->body.key.eventTime = body.key.eventTime; // int32_t deviceId @@ -172,6 +170,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::MOTION: { // uint32_t seq msg->body.motion.seq = body.motion.seq; + // int32_t eventId + msg->body.motion.eventId = body.motion.eventId; // nsecs_t eventTime msg->body.motion.eventTime = body.motion.eventTime; // int32_t deviceId @@ -238,6 +238,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { } case InputMessage::Type::FOCUS: { msg->body.focus.seq = body.focus.seq; + msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; msg->body.focus.inTouchMode = body.focus.inTouchMode; break; @@ -436,11 +437,12 @@ InputPublisher::InputPublisher(const sp& channel) : InputPublisher::~InputPublisher() { } -status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, - int32_t displayId, std::array hmac, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime, nsecs_t eventTime) { +status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, + int32_t source, int32_t displayId, + std::array hmac, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, int32_t repeatCount, nsecs_t downTime, + nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); @@ -462,6 +464,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t InputMessage msg; msg.header.type = InputMessage::Type::KEY; msg.body.key.seq = seq; + msg.body.key.eventId = eventId; msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; @@ -478,7 +481,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t } status_t InputPublisher::publishMotionEvent( - uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -519,6 +522,7 @@ status_t InputPublisher::publishMotionEvent( InputMessage msg; msg.header.type = InputMessage::Type::MOTION; msg.body.motion.seq = seq; + msg.body.motion.eventId = eventId; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; @@ -549,7 +553,8 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) { +status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, + bool inTouchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", @@ -561,6 +566,7 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inT InputMessage msg; msg.header.type = InputMessage::Type::FOCUS; msg.body.focus.seq = seq; + msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus ? 1 : 0; msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; return mChannel->sendMessage(&msg); @@ -1146,7 +1152,7 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.key.deviceId, msg->body.key.source, + event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, @@ -1154,7 +1160,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.focus.hasFocus == 1, + event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); } @@ -1167,17 +1173,16 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.motion.deviceId, - msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, - msg->body.motion.action, msg->body.motion.actionButton, - msg->body.motion.flags, msg->body.motion.edgeFlags, - msg->body.motion.metaState, msg->body.motion.buttonState, - msg->body.motion.classification, msg->body.motion.xScale, - msg->body.motion.yScale, msg->body.motion.xOffset, msg->body.motion.yOffset, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, + msg->body.motion.actionButton, msg->body.motion.flags, + msg->body.motion.edgeFlags, msg->body.motion.metaState, + msg->body.motion.buttonState, msg->body.motion.classification, + msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, + msg->body.motion.yOffset, msg->body.motion.xPrecision, + msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, + msg->body.motion.yCursorPosition, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 885196f3f3..8e2eec85ed 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -73,6 +73,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr int32_t deviceId = 1; constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; @@ -88,8 +89,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, + status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action, + flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -107,6 +108,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { KeyEvent* keyEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, keyEvent->getId()); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); @@ -139,6 +141,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr int32_t deviceId = 1; constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; @@ -182,7 +185,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action, + status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, xScale, yScale, xOffset, yOffset, xPrecision, yPrecision, xCursorPosition, @@ -204,6 +207,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { MotionEvent* motionEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, motionEvent->getId()); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); @@ -277,10 +281,11 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { status_t status; constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; constexpr bool inTouchMode = true; - status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode); + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; @@ -294,6 +299,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { FocusEvent* focusEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, focusEvent->getId()); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); @@ -332,8 +338,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, @@ -348,8 +354,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, @@ -369,8 +375,8 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 1 /* xScale */, + status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index dd127fcabd..1fe7bb90ca 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -35,6 +35,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage, body, 8); CHECK_OFFSET(InputMessage::Body::Key, seq, 0); + CHECK_OFFSET(InputMessage::Body::Key, eventId, 4); CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); @@ -49,6 +50,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); + CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -74,8 +76,9 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); - CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); @@ -95,7 +98,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); - static_assert(sizeof(InputMessage::Body::Focus) == 8); + static_assert(sizeof(InputMessage::Body::Focus) == 16); } // --- VerifiedInputEvent --- diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 439d9bf312..4ec4e25010 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -108,6 +108,7 @@ cc_defaults { srcs: [":libinputflinger_base_sources"], shared_libs: [ "libbase", + "libcutils", "libinput", "liblog", "libutils", diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index e91e803fb8..84838ec8a7 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -16,12 +16,18 @@ #define LOG_TAG "InputListener" +#define ATRACE_TAG ATRACE_TAG_INPUT + //#define LOG_NDEBUG 0 #include "InputListener.h" +#include #include #include +#include + +using android::base::StringPrintf; namespace android { @@ -228,6 +234,13 @@ void NotifyDeviceResetArgs::notify(const sp& listener) c // --- QueuedInputListener --- +static inline void traceEvent(const char* functionName, int32_t id) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("%s(id=0x%" PRIx32 ")", functionName, id); + ATRACE_NAME(message.c_str()); + } +} + QueuedInputListener::QueuedInputListener(const sp& innerListener) : mInnerListener(innerListener) { } @@ -241,22 +254,27 @@ QueuedInputListener::~QueuedInputListener() { void QueuedInputListener::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args)); } void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyKeyArgs(*args)); } void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyMotionArgs(*args)); } void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifySwitchArgs(*args)); } void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + traceEvent(__func__, args->id); mArgsQueue.push_back(new NotifyDeviceResetArgs(*args)); } diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index 6f82f4fd10..188212bccf 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -20,11 +20,13 @@ namespace android::inputdispatcher { -Connection::Connection(const sp& inputChannel, bool monitor) +Connection::Connection(const sp& inputChannel, bool monitor, + const IdGenerator& idGenerator) : status(STATUS_NORMAL), inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), + inputState(idGenerator), inputPublisherBlocked(false) {} Connection::~Connection() {} diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index 8423010502..bb3f2fee19 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -58,7 +58,7 @@ public: // yet received a "finished" response from the application. std::deque waitQueue; - explicit Connection(const sp& inputChannel, bool monitor); + Connection(const sp& inputChannel, bool monitor, const IdGenerator& idGenerator); inline const std::string getInputChannelName() const { return inputChannel->getName(); } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index c58ae23cc5..ab481bd411 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -29,9 +29,6 @@ namespace android::inputdispatcher { -// Sequence number for synthesized or injected events. -constexpr int32_t SYNTHESIZED_EVENT_ID = 0; - struct EventEntry { enum class Type { CONFIGURATION_CHANGED, @@ -78,7 +75,9 @@ struct EventEntry { * Key repeat is a synthesized event, because it is related to an actual hardware state * (a key is currently pressed), but the repeat itself is generated by the framework. */ - inline bool isSynthesized() const { return isInjected() || id == SYNTHESIZED_EVENT_ID; } + inline bool isSynthesized() const { + return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER; + } void release(); @@ -199,7 +198,8 @@ struct DispatchEntry { float windowYScale = 1.0f; nsecs_t deliveryTime; // time when the event was actually delivered - // Set to the resolved action and flags when the event is enqueued. + // Set to the resolved ID, action and flags when the event is enqueued. + int32_t resolvedEventId; int32_t resolvedAction; int32_t resolvedFlags; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 2f7b5ad9e6..308d19b085 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -383,6 +383,7 @@ InputDispatcher::InputDispatcher(const sp& polic : mPolicy(policy), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), + mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), @@ -925,12 +926,13 @@ KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); if (entry->refCount == 1) { entry->recycle(); + entry->id = mIdGenerator.nextId(); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { KeyEntry* newEntry = - new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, entry->deviceId, entry->source, + new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source, entry->displayId, policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount + 1, entry->downTime); @@ -981,7 +983,7 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { FocusEntry* focusEntry = - new FocusEntry(SYNTHESIZED_EVENT_ID, now(), window.getToken(), hasFocus); + new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus); enqueueInboundEventLocked(focusEntry); } @@ -2188,7 +2190,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=%" PRIx32 ")", + StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")", connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } @@ -2244,7 +2246,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = - StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=%" PRIx32 ")", + StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")", connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } @@ -2299,6 +2301,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio switch (newEntry->type) { case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast(*newEntry); + dispatchEntry->resolvedEventId = keyEntry.id; dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2315,6 +2318,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast(*newEntry); + // Assign a default value to dispatchEntry that will never be generated by InputReader, + // and assign a InputDispatcher value if it doesn't change in the if-else chain below. + constexpr int32_t DEFAULT_RESOLVED_EVENT_ID = + static_cast(IdGenerator::Source::OTHER); + dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID; if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2327,6 +2335,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; } else { dispatchEntry->resolvedAction = motionEntry.action; + dispatchEntry->resolvedEventId = motionEntry.id; } if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source, @@ -2357,6 +2366,17 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio return; // skip the inconsistent event } + dispatchEntry->resolvedEventId = + dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID + ? mIdGenerator.nextId() + : motionEntry.id; + if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) { + std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32 + ") to MotionEvent(id=0x%" PRIx32 ").", + motionEntry.id, dispatchEntry->resolvedEventId); + ATRACE_NAME(message.c_str()); + } + dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget.inputChannel->getConnectionToken()); @@ -2438,14 +2458,16 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, std::array hmac = mHmacKeyManager.sign(verifiedEvent); // Publish the key event. - status = connection->inputPublisher - .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, - keyEntry->source, keyEntry->displayId, - std::move(hmac), dispatchEntry->resolvedAction, - dispatchEntry->resolvedFlags, keyEntry->keyCode, - keyEntry->scanCode, keyEntry->metaState, - keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); + status = + connection->inputPublisher + .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId, + keyEntry->deviceId, keyEntry->source, + keyEntry->displayId, std::move(hmac), + dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags, keyEntry->keyCode, + keyEntry->scanCode, keyEntry->metaState, + keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); break; } @@ -2494,9 +2516,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Publish the motion event. status = connection->inputPublisher - .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, - motionEntry->source, motionEntry->displayId, - std::move(hmac), dispatchEntry->resolvedAction, + .publishMotionEvent(dispatchEntry->seq, + dispatchEntry->resolvedEventId, + motionEntry->deviceId, motionEntry->source, + motionEntry->displayId, std::move(hmac), + dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, @@ -2515,6 +2539,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::FOCUS: { FocusEntry* focusEntry = static_cast(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, + focusEntry->id, focusEntry->hasFocus, mInTouchMode); break; @@ -2920,10 +2945,17 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotion } } + int32_t newId = mIdGenerator.nextId(); + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32 + ") to MotionEvent(id=0x%" PRIx32 ").", + originalMotionEntry.id, newId); + ATRACE_NAME(message.c_str()); + } MotionEntry* splitMotionEntry = - new MotionEntry(originalMotionEntry.id, originalMotionEntry.eventTime, - originalMotionEntry.deviceId, originalMotionEntry.source, - originalMotionEntry.displayId, originalMotionEntry.policyFlags, action, + new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId, + originalMotionEntry.source, originalMotionEntry.displayId, + originalMotionEntry.policyFlags, action, originalMotionEntry.actionButton, originalMotionEntry.flags, originalMotionEntry.metaState, originalMotionEntry.buttonState, originalMotionEntry.classification, originalMotionEntry.edgeFlags, @@ -4225,7 +4257,7 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan return BAD_VALUE; } - sp connection = new Connection(inputChannel, false /*monitor*/); + sp connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator); int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; @@ -4254,7 +4286,7 @@ status_t InputDispatcher::registerInputMonitor(const sp& inputChan return BAD_VALUE; } - sp connection = new Connection(inputChannel, true /*monitor*/); + sp connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator); const int fd = inputChannel->getFd(); mConnectionsByFd[fd] = connection; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 482133ee90..4aa47f89f0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -156,6 +156,8 @@ private: DropReason mLastDropReason GUARDED_BY(mLock); + const IdGenerator mIdGenerator; + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 7fa9e09aad..386056d9b2 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -16,9 +16,11 @@ #include "InputState.h" +#include "InputDispatcher.h" + namespace android::inputdispatcher { -InputState::InputState() {} +InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {} InputState::~InputState() {} @@ -268,7 +270,7 @@ std::vector InputState::synthesizeCancelationEvents( std::vector events; for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { - events.push_back(new KeyEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, @@ -281,7 +283,7 @@ std::vector InputState::synthesizeCancelationEvents( if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, @@ -331,7 +333,7 @@ std::vector InputState::synthesizePointerDownEvents(nsecs_t current : AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - events.push_back(new MotionEntry(SYNTHESIZED_EVENT_ID, currentTime, memento.deviceId, + events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 08266ae9cd..d97a664d74 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -30,7 +30,7 @@ static constexpr int32_t INVALID_POINTER_INDEX = -1; * synthesized when events are dropped. */ class InputState { public: - InputState(); + explicit InputState(const IdGenerator& idGenerator); ~InputState(); // Returns true if there is no state to be canceled. @@ -111,6 +111,8 @@ private: void mergePointerStateTo(MotionMemento& other) const; }; + const IdGenerator& mIdGenerator; // InputDispatcher owns it so we won't have dangling reference. + std::vector mKeyMementos; std::vector mMotionMementos; KeyedVector mFallbackKeys; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 2998cc9619..657a134865 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -46,7 +46,6 @@ InputReader::InputReader(std::shared_ptr eventHub, : mContext(this), mEventHub(eventHub), mPolicy(policy), - mNextId(1), mGlobalMetaState(0), mGeneration(1), mNextInputDeviceId(END_RESERVED_ID), @@ -697,7 +696,8 @@ void InputReader::monitor() { // --- InputReader::ContextImpl --- -InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {} +InputReader::ContextImpl::ContextImpl(InputReader* reader) + : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {} void InputReader::ContextImpl::updateGlobalMetaState() { // lock is already held by the input loop @@ -762,7 +762,7 @@ EventHubInterface* InputReader::ContextImpl::getEventHub() { } int32_t InputReader::ContextImpl::getNextId() { - return (mReader->mNextId)++; + return mIdGenerator.nextId(); } } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index eaa105a8f6..693ec30b7d 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -94,6 +94,7 @@ protected: class ContextImpl : public InputReaderContext { InputReader* mReader; + IdGenerator mIdGenerator; public: explicit ContextImpl(InputReader* reader); @@ -132,9 +133,6 @@ private: InputReaderConfiguration mConfig; - // used by InputReaderContext::getNextId() as a counter for event sequence numbers - uint32_t mNextId; - // The event queue. static const int EVENT_BUFFER_SIZE = 256; RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 270f891d36..d30c4f1635 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,13 +16,18 @@ #include "../dispatcher/InputDispatcher.h" +#include #include #include #include #include +#include +#include #include +using android::base::StringPrintf; + namespace android::inputdispatcher { // An arbitrary time value. @@ -107,6 +112,11 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { + mConfig.keyRepeatTimeout = timeout; + mConfig.keyRepeatDelay = delay; + } + private: std::unique_ptr mFilteredEvent; std::optional mConfigurationChangedTime; @@ -1443,6 +1453,105 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { ASSERT_EQ(0, verifiedKey.repeatCount); } +class InputDispatcherKeyRepeatTest : public InputDispatcherTest { +protected: + static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms + static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms + + sp mApp; + sp mWindow; + + virtual void SetUp() override { + mFakePolicy = new FakeInputDispatcherPolicy(); + mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); + mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + ASSERT_EQ(OK, mDispatcher->start()); + + setUpWindow(); + } + + void setUpWindow() { + mApp = new FakeApplicationHandle(); + mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mWindow->setFocus(true); + mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT); + + mWindow->consumeFocusEvent(true); + } + + void sendAndConsumeKeyDown() { + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + } + + void expectKeyRepeatOnce(int32_t repeatCount) { + SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount)); + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent); + + uint32_t eventType = repeatEvent->getType(); + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, eventType); + + KeyEvent* repeatKeyEvent = static_cast(repeatEvent); + uint32_t eventAction = repeatKeyEvent->getAction(); + EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction); + EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount()); + } + + void sendAndConsumeKeyUp() { + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + mWindow->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, + 0 /*expectedFlags*/); + } +}; + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) { + sendAndConsumeKeyDown(); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + expectKeyRepeatOnce(repeatCount); + } +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) { + sendAndConsumeKeyDown(); + expectKeyRepeatOnce(1 /*repeatCount*/); + sendAndConsumeKeyUp(); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) { + sendAndConsumeKeyDown(); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER, + IdGenerator::getSource(repeatEvent->getId())); + } +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) { + sendAndConsumeKeyDown(); + + std::unordered_set idSet; + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + InputEvent* repeatEvent = mWindow->consume(); + ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + int32_t id = repeatEvent->getId(); + EXPECT_EQ(idSet.end(), idSet.find(id)); + idSet.insert(id); + } +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 46cd4809fc..3ae8b56c11 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1642,7 +1642,7 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { ASSERT_EQ(1, event.value); } -TEST_F(InputReaderTest, DeviceReset_IncrementsId) { +TEST_F(InputReaderTest, DeviceReset_RandomId) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; constexpr int32_t eventHubId = 1; @@ -1659,22 +1659,37 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsId) { disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; enableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; disableDevice(deviceId); mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_TRUE(prevId < resetArgs.id); + ASSERT_NE(prevId, resetArgs.id); prevId = resetArgs.id; } +TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) { + constexpr int32_t deviceId = 1; + constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr int32_t eventHubId = 1; + std::shared_ptr device = mReader->newDevice(deviceId, "fake"); + // Must add at least one mapper or the device will be ignored! + device->addMapper(eventHubId, AINPUT_SOURCE_KEYBOARD); + mReader->setNextDevice(device); + ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(IdGenerator::Source::INPUT_READER, IdGenerator::getSource(resetArgs.id)); +} + TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; @@ -1821,14 +1836,14 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { keyboard->pressAndReleaseHomeKey(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_LT(prevId, keyArgs.id); + ASSERT_NE(prevId, keyArgs.id); prevId = keyArgs.id; ASSERT_LE(prevTimestamp, keyArgs.eventTime); prevTimestamp = keyArgs.eventTime; ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_LT(prevId, keyArgs.id); + ASSERT_NE(prevId, keyArgs.id); ASSERT_LE(prevTimestamp, keyArgs.eventTime); } -- cgit v1.2.3-59-g8ed1b From aab256296f079e0cdec39b85c879afbec1c1a847 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 16 Jan 2020 11:22:11 +0800 Subject: Add TouchIntegrationTest tests Base on InputReaderIntegrationTest which would test the threading functionality. This would emulate the touch screen device and send the raw events to verify if InputListener could really receive the MotionEvent. - Add tests for handling single touch and multi touch. - Add test for handling MT_TOOL_PALM. Bug: 117933934 Test: atest TouchIntegrationTest Change-Id: I450c0159ac60d7da0471235407b3c537ab94fa6f --- services/inputflinger/tests/InputReader_test.cpp | 132 +++++++++++++++++++++- services/inputflinger/tests/TestInputListener.cpp | 19 +--- services/inputflinger/tests/TestInputListener.h | 5 +- services/inputflinger/tests/UinputDevice.cpp | 69 ++++++++++- services/inputflinger/tests/UinputDevice.h | 36 ++++++ 5 files changed, 243 insertions(+), 18 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 3ae8b56c11..618aefc741 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1748,7 +1748,7 @@ protected: virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); - mTestListener = new TestInputListener(); + mTestListener = new TestInputListener(50ms); mReader = new InputReader(std::make_shared(), mFakePolicy, mTestListener); ASSERT_EQ(mReader->start(), OK); @@ -1847,6 +1847,135 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { ASSERT_LE(prevTimestamp, keyArgs.eventTime); } +// --- TouchProcessTest --- +class TouchIntegrationTest : public InputReaderIntegrationTest { +protected: + static const int32_t FIRST_SLOT = 0; + static const int32_t SECOND_SLOT = 1; + static const int32_t FIRST_TRACKING_ID = 0; + static const int32_t SECOND_TRACKING_ID = 1; + const std::string UNIQUE_ID = "local:0"; + + virtual void SetUp() override { + InputReaderIntegrationTest::SetUp(); + // At least add an internal display. + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT, + ViewportType::VIEWPORT_INTERNAL); + + mDevice = createUinputDevice(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + } + + void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, + int32_t orientation, const std::string& uniqueId, + std::optional physicalPort, + ViewportType viewportType) { + mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId, + physicalPort, viewportType); + mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + } + + std::unique_ptr mDevice; +}; + +TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { + NotifyMotionArgs args; + const Point centerPoint = mDevice->getCenterPoint(); + + // ACTION_DOWN + mDevice->sendDown(centerPoint); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); + + // ACTION_MOVE + mDevice->sendMove(centerPoint + Point(1, 1)); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + + // ACTION_UP + mDevice->sendUp(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); +} + +TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) { + NotifyMotionArgs args; + const Point centerPoint = mDevice->getCenterPoint(); + + // ACTION_DOWN + mDevice->sendDown(centerPoint); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); + + // ACTION_POINTER_DOWN (Second slot) + const Point secondPoint = centerPoint + Point(100, 100); + mDevice->sendSlot(SECOND_SLOT); + mDevice->sendTrackingId(SECOND_TRACKING_ID); + mDevice->sendDown(secondPoint + Point(1, 1)); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + args.action); + + // ACTION_MOVE (Second slot) + mDevice->sendMove(secondPoint); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + + // ACTION_POINTER_UP (Second slot) + mDevice->sendUp(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + args.action); + + // ACTION_UP + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendUp(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); +} + +TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { + NotifyMotionArgs args; + const Point centerPoint = mDevice->getCenterPoint(); + + // ACTION_DOWN + mDevice->sendDown(centerPoint); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); + + // ACTION_POINTER_DOWN (Second slot) + const Point secondPoint = centerPoint + Point(100, 100); + mDevice->sendSlot(SECOND_SLOT); + mDevice->sendTrackingId(SECOND_TRACKING_ID); + mDevice->sendDown(secondPoint); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + args.action); + + // ACTION_MOVE (Second slot) + mDevice->sendMove(secondPoint + Point(1, 1)); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + + // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event. + // Expect to receive ACTION_CANCEL, to abort the entire gesture. + mDevice->sendToolType(MT_TOOL_PALM); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action); + + // ACTION_POINTER_UP (Second slot) + mDevice->sendUp(); + + // ACTION_UP + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendUp(); + + // Expect no event received after abort the entire gesture. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); +} + // --- InputDeviceTest --- class InputDeviceTest : public testing::Test { protected: @@ -7032,5 +7161,4 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); } - } // namespace android diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 06b05acdab..86ff3b1d4d 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -19,20 +19,11 @@ #include "TestInputListener.h" -namespace { - -using std::chrono_literals::operator""ms; - -// Timeout for waiting for an expected event -static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms; - -} // namespace - namespace android { // --- TestInputListener --- -TestInputListener::TestInputListener() { } +TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {} TestInputListener::~TestInputListener() { } @@ -95,9 +86,9 @@ void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string m std::vector& queue = std::get>(mQueues); if (queue.empty()) { - const bool eventReceived = - mCondition.wait_for(lock, WAIT_TIMEOUT, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (!eventReceived) { FAIL() << "Timed out waiting for event: " << message.c_str(); } @@ -114,7 +105,7 @@ void TestInputListener::assertNotCalled(std::string message) { base::ScopedLockAssertion assumeLocked(mLock); std::vector& queue = std::get>(mQueues); - const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) { + const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) { return !queue.empty(); }); if (eventReceived) { diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 945e2ea58c..4262f5adfb 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -21,6 +21,8 @@ #include #include "InputListener.h" +using std::chrono_literals::operator""ms; + namespace android { // --- TestInputListener --- @@ -30,7 +32,7 @@ protected: virtual ~TestInputListener(); public: - TestInputListener(); + TestInputListener(const std::chrono::milliseconds timeout = 5ms); void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); @@ -73,6 +75,7 @@ private: std::mutex mLock; std::condition_variable mCondition; + const std::chrono::milliseconds mTimeout; std::tuple, // std::vector, // diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 2775d21ce2..99480b71db 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -119,7 +119,7 @@ void UinputKeyboard::pressAndReleaseKey(int key) { EXPECT_NO_FATAL_FAILURE(releaseKey(key)); } -// --- UinputHomeKey--- +// --- UinputHomeKey --- UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} @@ -127,4 +127,71 @@ void UinputHomeKey::pressAndReleaseHomeKey() { EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME)); } +// --- UinputTouchScreen --- +UinputTouchScreen::UinputTouchScreen(const Rect* size) + : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} + +void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { + // Setup the touch screen device + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + + device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; + device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; + device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN; + device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX; + device->absmin[ABS_MT_POSITION_X] = mSize.left; + device->absmax[ABS_MT_POSITION_X] = mSize.right - 1; + device->absmin[ABS_MT_POSITION_Y] = mSize.top; + device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1; + device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN; + device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX; +} + +void UinputTouchScreen::sendSlot(int32_t slot) { + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot)); +} + +void UinputTouchScreen::sendTrackingId(int32_t trackingId) { + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId)); +} + +void UinputTouchScreen::sendDown(const Point& point) { + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +void UinputTouchScreen::sendMove(const Point& point) { + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +void UinputTouchScreen::sendUp() { + sendTrackingId(0xffffffff); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +void UinputTouchScreen::sendToolType(int32_t toolType) { + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType)); + EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); +} + +// Get the center x, y base on the range definition. +const Point UinputTouchScreen::getCenterPoint() { + return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2); +} + } // namespace android diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index 57d9011695..ec3cd9fdba 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include @@ -106,6 +108,40 @@ private: UinputHomeKey(); }; +// --- UinputTouchScreen --- +// A touch screen device with specific size. +class UinputTouchScreen : public UinputDevice { +public: + static constexpr const char* DEVICE_NAME = "Test Touch Screen"; + static const int32_t RAW_TOUCH_MIN = 0; + static const int32_t RAW_TOUCH_MAX = 31; + static const int32_t RAW_ID_MIN = 0; + static const int32_t RAW_ID_MAX = 9; + static const int32_t RAW_SLOT_MIN = 0; + static const int32_t RAW_SLOT_MAX = 9; + static const int32_t RAW_PRESSURE_MIN = 0; + static const int32_t RAW_PRESSURE_MAX = 255; + + template + friend std::unique_ptr createUinputDevice(Ts... args); + + void sendSlot(int32_t slot); + void sendTrackingId(int32_t trackingId); + void sendDown(const Point& point); + void sendMove(const Point& point); + void sendUp(); + void sendToolType(int32_t toolType); + + const Point getCenterPoint(); + +protected: + UinputTouchScreen(const Rect* size); + +private: + void configureDevice(int fd, uinput_user_dev* device) override; + const Rect mSize; +}; + } // namespace android #endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H -- cgit v1.2.3-59-g8ed1b From e3021d703130f9f28a94bef0e7179e75794a59cf Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 28 Feb 2020 15:25:41 -0800 Subject: Remove device state when device is reset When device reset is received in MotionClassifier, don't just set the classification for that device to 'none'. Instead, remove all state related to that device. This prevents growing indefinitely the unordered_maps that contain device-related state. Also, log the feature state to the InputClassifier dump. Bug: 150419367 Test: run several touch integration tests several times, and then do 'dumpsys input'. Ensure that the list doesn't grow as the tests are executed multiple times. Change-Id: I2e1ac359458a321f87e4599bb19350623afc9b2b --- services/inputflinger/BlockingQueue.h | 5 +---- services/inputflinger/InputClassifier.cpp | 9 ++++++++- services/inputflinger/InputClassifier.h | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h index db9f26ec12..b612ca77ce 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/services/inputflinger/BlockingQueue.h @@ -45,10 +45,7 @@ public: T pop() { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLock(mLock); - mHasElements.wait(lock, [this]{ - android::base::ScopedLockAssertion assumeLock(mLock); - return !this->mQueue.empty(); - }); + mHasElements.wait(lock, [this]() REQUIRES(mLock) { return !this->mQueue.empty(); }); T t = std::move(mQueue.front()); mQueue.erase(mQueue.begin()); return t; diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index ae9a34882e..e5e83d752c 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -250,7 +250,7 @@ void MotionClassifier::callInputClassifierHal() { case ClassifierEventType::DEVICE_RESET: { const int32_t deviceId = *(event.getDeviceId()); halResponseOk = mService->resetDevice(deviceId).isOk(); - setClassification(deviceId, MotionClassification::NONE); + clearDeviceState(deviceId); break; } case ClassifierEventType::HAL_RESET: { @@ -321,6 +321,12 @@ void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) { mClassifications[deviceId] = MotionClassification::NONE; } +void MotionClassifier::clearDeviceState(int32_t deviceId) { + std::scoped_lock lock(mLock); + mClassifications.erase(deviceId); + mLastDownTimes.erase(deviceId); +} + MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) { updateLastDownTime(args.deviceId, args.downTime); @@ -455,6 +461,7 @@ void InputClassifier::serviceDied(uint64_t /*cookie*/, void InputClassifier::dump(std::string& dump) { std::scoped_lock lock(mLock); dump += "Input Classifier State:\n"; + dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled"); dump += INDENT1 "Motion Classifier:\n"; if (mMotionClassifier) { diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 47e20dbf75..96923526da 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -212,6 +212,8 @@ private: void updateLastDownTime(int32_t deviceId, nsecs_t downTime); + void clearDeviceState(int32_t deviceId); + /** * Exit the InputClassifier HAL thread. * Useful for tests to ensure proper cleanup. -- cgit v1.2.3-59-g8ed1b From 47040bfbf505ca6865f05b568d428f796856ad29 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 28 Feb 2020 15:03:13 -0800 Subject: Test MotionEvents for InputDispatcher::verifyInputEvent We recently added a new api, InputDispatcher::verifyInputEvent. There are currently tests for this api for key events. However, a motion event test is currently missing. Add the missing test here. Bug: 149845127 Test: atest inputflinger_tests Change-Id: I791a789a9df4d62d4100b61611b52a98c77c4787 --- .../inputflinger/tests/InputDispatcher_test.cpp | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index d30c4f1635..1f283b18ac 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1445,7 +1445,6 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { ASSERT_EQ(keyArgs.action, verifiedKey.action); ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos); - ASSERT_EQ(keyArgs.eventTime, verifiedKey.eventTimeNanos); ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags); ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode); ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode); @@ -1453,6 +1452,43 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { ASSERT_EQ(0, verifiedKey.repeatCount); } +TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + + InputEvent* event = window->consume(); + ASSERT_NE(event, nullptr); + + std::unique_ptr verified = mDispatcher->verifyInputEvent(*event); + ASSERT_NE(verified, nullptr); + ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION); + + EXPECT_EQ(motionArgs.eventTime, verified->eventTimeNanos); + EXPECT_EQ(motionArgs.deviceId, verified->deviceId); + EXPECT_EQ(motionArgs.source, verified->source); + EXPECT_EQ(motionArgs.displayId, verified->displayId); + + const VerifiedMotionEvent& verifiedMotion = static_cast(*verified); + + EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX); + EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY); + EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked); + EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); + EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags); + EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState); + EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms -- cgit v1.2.3-59-g8ed1b From 1652397ab77904092509a0315ad026f0b4f10a18 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 4 Mar 2020 17:48:39 -0800 Subject: Properly initialize MotionClassifier InputClassifier will now play a more active role in managing the lifecycle of MotionClassifier. In the previous version of the code, we had a circular reference: MotionClassifier owned a wp, that sometimes got promoted to sp<>. But the InputClassifier was the official owner of MotionClassifier, and owned a unique_ptr. The owner of InputClassifier in real code is InputManager, and in test code, it's the test class. When the owner of InputClassifier destroyed InputClassifier, this could sometimes happen at the time when the MotionClassifier also held a reference to the InputClassifier. That meant that the proper owner of InputClassifier was not invoking the destructor. Instead, the MotionClassifier was invoking the InputClassifier destructor. To fix the situation, we now do the following: 1. InputClassifier will never die before MotionClassifier. 2. MotionClassifier constructor is now allowed to block. It would block for some time because calling getService takes a while. To account for this, InputClassifier launches a new thread to create MotionClassifier. 3. When MotionClassifier is ready to process events, InputClassifier updates mMotionClassifier, which makes it non-null. 4. We now create a separate death recipient, which is co-owned by InputClassifier and MotionClassifier. This is done so that the refcount of the deathrecipient does not affect the refcount of InputClassifier, and thus enforces the ownership of InputClassifier by the InputManager. Now, no one can call ~InputClassifier except for its real owner. 5. MotionClassifier will subscribe the death recipient to the death of the HAL. InputClassifier will delete MotionClassifier if HAL dies. MotionClassifier no longer holds on to the death recipient. 6. We move the loop of the MotionClassifier thread to focus only on processing events. That thread will no longer do any initialization. 7. Remove the thread check inside MotionClassifier. It isn't really useful, now that there's only 1 function for the entire thread. Ownership summary: Both InputClassifier and MotionClassifier own DeathRecipient. DeathRecipient has a reference to InputClassifier. Thus, we must guarantee that DeathRecipient dies before InputClassifier. InputClassifier owns MotionClassifier. This is OK, since even if InputClassifier dies, it will first delete MotionClassifier. That will cause MotionClassifier to release 1 refCount from DeathRecipient. That means the only reference remaining to DeathRecipient will be inside InputClassifier, so InputClassifier will always be alive until DeathRecipient is dead. Similar argument applies if MotionClassifier and DeathRecipient die in different order (as observed from InputClassifier). Tests: 1) Manually running inputflinger_tests on cuttlefish: build/launch cuttlefish using go/acloud m inputflinger_tests adb push out/target/product/vsoc_x86/data/nativetest/inputflinger_tests/inputflinger_tests /data/nativetest/inputflinger_tests/inputflinger_tests adb shell /data/nativetest/inputflinger_tests # ./inputflinger_tests --gtest_filter=*InputClassifierTest* --gtest_repeat=1000 --gtest_break_on_failure 2) Boot flame and open logcat. Observe in logcat: StartInputManagerService took to complete: 2ms Previously, in synchronous approach ( b/130184032) it was about 100 ms (so we did not regress). 3) Kill the HAL while system_server is running, and dumpsys input before and after: adb shell dumpsys input (observe that MotionClassifier is non-null on flame) adb shell -t killall android.hardware.input.classifier@1.0-service adb shell dumpsys input (observe that MotionCLassifier is null) Bug: 149155998 Test: see "Tests" section above Change-Id: Ic76b82bd5f2cd374e3b001400eb495ca36de7353 --- services/inputflinger/InputClassifier.cpp | 134 ++++++++------------- services/inputflinger/InputClassifier.h | 88 ++++++++------ .../inputflinger/tests/InputClassifier_test.cpp | 33 ++++- 3 files changed, 135 insertions(+), 120 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index e5e83d752c..8ba1f7f8c1 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -46,8 +46,6 @@ using namespace android::hardware::input; namespace android { -static constexpr bool DEBUG = false; - // Category (=namespace) name for the input settings that are applied at boot time static const char* INPUT_NATIVE_BOOT = "input_native_boot"; // Feature flag name for the deep press feature @@ -141,53 +139,46 @@ std::optional ClassifierEvent::getDeviceId() const { // --- MotionClassifier --- -MotionClassifier::MotionClassifier(sp deathRecipient) : - mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) { - mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); +MotionClassifier::MotionClassifier( + sp service) + : mEvents(MAX_EVENTS), mService(service) { + // Under normal operation, we do not need to reset the HAL here. But in the case where system + // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already + // have received events in the past. That means, that HAL could be in an inconsistent state + // once it receives events from the newly created MotionClassifier. + mEvents.push(ClassifierEvent::createHalResetEvent()); + + mHalThread = std::thread(&MotionClassifier::processEvents, this); #if defined(__linux__) // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif } -/** - * This function may block for some time to initialize the HAL, so it should only be called - * from the "InputClassifier HAL" thread. - */ -bool MotionClassifier::init() { - ensureHalThread(__func__); +std::unique_ptr MotionClassifier::create( + sp deathRecipient) { + if (!deepPressEnabled()) { + // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. + // When MotionClassifier is null, InputClassifier will forward all events + // to the next InputListener, unmodified. + return nullptr; + } sp service = classifier::V1_0::IInputClassifier::getService(); if (!service) { // Not really an error, maybe the device does not have this HAL, // but somehow the feature flag is flipped ALOGI("Could not obtain InputClassifier HAL"); - return false; + return nullptr; } - sp recipient = mDeathRecipient.promote(); - if (recipient != nullptr) { - const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false); - if (!linked) { - ALOGE("Could not link MotionClassifier to the HAL death"); - return false; - } - } - - // Under normal operation, we do not need to reset the HAL here. But in the case where system - // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already - // have received events in the past. That means, that HAL could be in an inconsistent state - // once it receives events from the newly created MotionClassifier. - mEvents.push(ClassifierEvent::createHalResetEvent()); - - { - std::scoped_lock lock(mLock); - if (mService) { - ALOGE("MotionClassifier::%s should only be called once", __func__); - } - mService = service; + const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link death recipient to the HAL death"); + return nullptr; } - return true; + // Using 'new' to access a non-public constructor + return std::unique_ptr(new MotionClassifier(service)); } MotionClassifier::~MotionClassifier() { @@ -195,14 +186,6 @@ MotionClassifier::~MotionClassifier() { mHalThread.join(); } -void MotionClassifier::ensureHalThread(const char* function) { - if (DEBUG) { - if (std::this_thread::get_id() != mHalThread.get_id()) { - LOG_FATAL("Function %s should only be called from InputClassifier thread", function); - } - } -} - /** * Obtain the classification from the HAL for a given MotionEvent. * Should only be called from the InputClassifier thread (mHalThread). @@ -213,23 +196,7 @@ void MotionClassifier::ensureHalThread(const char* function) { * To remove any possibility of negatively affecting the touch latency, the HAL * is called from a dedicated thread. */ -void MotionClassifier::callInputClassifierHal() { - ensureHalThread(__func__); - const bool initialized = init(); - if (!initialized) { - // MotionClassifier no longer useful. - // Deliver death notification from a separate thread - // because ~MotionClassifier may be invoked, which calls mHalThread.join() - std::thread([deathRecipient = mDeathRecipient](){ - sp recipient = deathRecipient.promote(); - if (recipient != nullptr) { - recipient->serviceDied(0 /*cookie*/, nullptr); - } - }).detach(); - return; - } - // From this point on, mService is guaranteed to be non-null. - +void MotionClassifier::processEvents() { while (true) { ClassifierEvent event = mEvents.pop(); bool halResponseOk = true; @@ -389,24 +356,30 @@ void MotionClassifier::dump(std::string& dump) { } } +// --- HalDeathRecipient -// --- InputClassifier --- +InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {} -InputClassifier::InputClassifier(const sp& listener) : - mListener(listener) { - // The rest of the initialization is done in onFirstRef, because we need to obtain - // an sp to 'this' in order to register for HAL death notifications +void InputClassifier::HalDeathRecipient::serviceDied( + uint64_t cookie, const wp& who) { + sp service = who.promote(); + if (service) { + service->unlinkToDeath(this); + } + mParent.setMotionClassifier(nullptr); } -void InputClassifier::onFirstRef() { - if (!deepPressEnabled()) { - // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. - // When MotionClassifier is null, InputClassifier will forward all events - // to the next InputListener, unmodified. - return; - } - std::scoped_lock lock(mLock); - mMotionClassifier = std::make_unique(this); +// --- InputClassifier --- + +InputClassifier::InputClassifier(const sp& listener) + : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) { + mInitializeMotionClassifierThread = std::thread( + [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); +#if defined(__linux__) + // Set the thread name for debugging + pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), + "Create MotionClassifier"); +#endif } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { @@ -447,15 +420,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } -void InputClassifier::serviceDied(uint64_t /*cookie*/, - const wp& who) { +void InputClassifier::setMotionClassifier( + std::unique_ptr motionClassifier) { std::scoped_lock lock(mLock); - ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); - mMotionClassifier = nullptr; - sp service = who.promote(); - if (service) { - service->unlinkToDeath(this); - } + mMotionClassifier = std::move(motionClassifier); } void InputClassifier::dump(std::string& dump) { @@ -472,4 +440,8 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } +InputClassifier::~InputClassifier() { + mInitializeMotionClassifierThread.join(); +} + } // namespace android diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 96923526da..8f586956e5 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -19,8 +19,8 @@ #include #include -#include #include +#include #include "BlockingQueue.h" #include "InputListener.h" @@ -113,23 +113,23 @@ protected: */ class MotionClassifier final : public MotionClassifierInterface { public: - /** - * The deathRecipient will be subscribed to the HAL death. If the death recipient - * owns MotionClassifier and receives HAL death, it should delete its copy of it. - * The callback serviceDied will also be sent if the MotionClassifier itself fails - * to initialize. If the MotionClassifier fails to initialize, it is not useful, and - * should be deleted. - * If no death recipient is supplied, then the registration step will be skipped, so there will - * be no listeners registered for the HAL death. This is useful for testing - * MotionClassifier in isolation. + /* + * Create an instance of MotionClassifier. + * The death recipient, if provided, will be subscribed to the HAL death. + * The death recipient could be used to destroy MotionClassifier. + * + * This function should be called asynchronously, because getService takes a long time. */ - explicit MotionClassifier(sp deathRecipient = nullptr); + static std::unique_ptr create( + sp deathRecipient); + ~MotionClassifier(); /** * Classifies events asynchronously; that is, it doesn't block events on a classification, - * but instead sends them over to the classifier HAL and after a classification is - * determined, it then marks the next event it sees in the stream with it. + * but instead sends them over to the classifier HAL. After a classification of a specific + * event is determined, MotionClassifier then marks the next event in the stream with this + * classification. * * Therefore, it is acceptable to have the classifications be delayed by 1-2 events * in a particular gesture. @@ -141,15 +141,9 @@ public: virtual void dump(std::string& dump) override; private: - /** - * Initialize MotionClassifier. - * Return true if initializaion is successful. - */ - bool init(); - /** - * Entity that will be notified of the HAL death (most likely InputClassifier). - */ - wp mDeathRecipient; + friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation + explicit MotionClassifier( + sp service); // The events that need to be sent to the HAL. BlockingQueue mEvents; @@ -164,14 +158,9 @@ private: */ std::thread mHalThread; /** - * Print an error message if the caller is not on the InputClassifier thread. - * Caller must supply the name of the calling function as __func__ + * Process events and call the InputClassifier HAL */ - void ensureHalThread(const char* function); - /** - * Call the InputClassifier HAL - */ - void callInputClassifierHal(); + void processEvents(); /** * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * When init() successfully completes, mService is guaranteed to remain non-null and to not @@ -225,19 +214,15 @@ private: const char* getServiceStatus() REQUIRES(mLock); }; - /** * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. */ -class InputClassifier : public InputClassifierInterface, - public android::hardware::hidl_death_recipient { +class InputClassifier : public InputClassifierInterface { public: explicit InputClassifier(const sp& listener); - // Some of the constructor logic is finished in onFirstRef - virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -245,17 +230,44 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - virtual void serviceDied(uint64_t cookie, - const wp& who) override; - virtual void dump(std::string& dump) override; + ~InputClassifier(); + private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; - std::unique_ptr mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to sp mListener; + + std::unique_ptr mMotionClassifier GUARDED_BY(mLock); + std::thread mInitializeMotionClassifierThread; + /** + * Set the value of mMotionClassifier. + * This is called from 2 different threads: + * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier + * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause + * mMotionClassifier to become nullptr. + */ + void setMotionClassifier(std::unique_ptr motionClassifier); + + /** + * The deathRecipient will call setMotionClassifier(null) when the HAL dies. + */ + class HalDeathRecipient : public android::hardware::hidl_death_recipient { + public: + explicit HalDeathRecipient(InputClassifier& parent); + virtual void serviceDied(uint64_t cookie, + const wp& who) override; + + private: + InputClassifier& mParent; + }; + // We retain a reference to death recipient, because the death recipient will be calling + // ~MotionClassifier if the HAL dies. + // If we don't retain a reference, and MotionClassifier is the only owner of the death + // recipient, the serviceDied call will cause death recipient to call its own destructor. + sp mHalDeathRecipient; }; } // namespace android diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 40086ef708..b4e755a595 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -22,6 +22,9 @@ #include using namespace android::hardware::input; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::input::common::V1_0::Classification; namespace android { @@ -132,6 +135,27 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { ASSERT_EQ(args, outArgs); } +/** + * A minimal implementation of IInputClassifier. + */ +struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier { + Return classify( + const android::hardware::input::common::V1_0::MotionEvent& event) override { + return Classification::NONE; + }; + Return reset() override { return Void(); }; + Return resetDevice(int32_t deviceId) override { return Void(); }; +}; + +/** + * An entity that will be subscribed to the HAL death. + */ +class TestDeathRecipient : public android::hardware::hidl_death_recipient { +public: + virtual void serviceDied(uint64_t cookie, + const wp& who) override{}; +}; + // --- MotionClassifierTest --- class MotionClassifierTest : public testing::Test { @@ -139,7 +163,14 @@ protected: std::unique_ptr mMotionClassifier; virtual void SetUp() override { - mMotionClassifier = std::make_unique(); + mMotionClassifier = MotionClassifier::create(new TestDeathRecipient()); + if (mMotionClassifier == nullptr) { + // If the device running this test does not have IInputClassifier service, + // use the test HAL instead. + // Using 'new' to access non-public constructor + mMotionClassifier = + std::unique_ptr(new MotionClassifier(new TestHal())); + } } }; -- cgit v1.2.3-59-g8ed1b From 2d112c5f625d09bb703b8909289d11f02ee44398 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Mon, 2 Mar 2020 17:46:00 +0800 Subject: Fix input infos are inconsistent between WMS and InputFlinger (2/2) In single focus system, the top focused display may change to the new one when receive a key event. And we would expect the focus could be changed before the event enqueued into inbound queue. This patch refactor 'setInputWindows' to prevent early callback and wakeup in first display, and make sure 'syncInputWindow' could wait until all input windows from all displays updated. Bug: 150250453 Test: atest libinput_tests inputflinger_tests Test: atest --rerun-until-failure 100 WindowFocusTests#testMovingDisplayToTopByKeyEvent Change-Id: I38f53d83738c4fbf570ea3b99e46341a5f9a4c0f --- services/inputflinger/InputManager.cpp | 6 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 173 ++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../dispatcher/include/InputDispatcherInterface.h | 7 +- .../inputflinger/tests/InputDispatcher_test.cpp | 62 ++++---- 6 files changed, 131 insertions(+), 127 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index c7c61cf1ef..f2a0014da4 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -111,8 +111,10 @@ void InputManager::setInputWindows(const std::vector& infos, handlesPerDisplay.emplace(info.displayId, std::vector>()); handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); } - for (auto const& i : handlesPerDisplay) { - mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener); + mDispatcher->setInputWindows(handlesPerDisplay); + + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); } } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 3b18813cef..7c5c9c5f0c 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -252,7 +252,7 @@ static void benchmarkNotifyMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(); @@ -288,7 +288,7 @@ static void benchmarkInjectMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); for (auto _ : state) { MotionEvent event = generateMotionEvent(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 308d19b085..4ec61b0c63 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3621,6 +3621,18 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( mWindowHandlesByDisplay[displayId] = newHandles; } +void InputDispatcher::setInputWindows( + const std::unordered_map>>& handlesPerDisplay) { + { // acquire lock + std::scoped_lock _l(mLock); + for (auto const& i : handlesPerDisplay) { + setInputWindowsLocked(i.second, i.first); + } + } + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... @@ -3628,9 +3640,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( * For focused handle, check if need to change and send a cancel event to previous one. * For removed handle, check if need to send a cancel event if already in touch. */ -void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, - int32_t displayId, - const sp& setInputWindowsListener) { +void InputDispatcher::setInputWindowsLocked( + const std::vector>& inputWindowHandles, int32_t displayId) { if (DEBUG_FOCUS) { std::string windowList; for (const sp& iwh : inputWindowHandles) { @@ -3638,109 +3649,97 @@ void InputDispatcher::setInputWindows(const std::vector>& } ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } - { // acquire lock - std::scoped_lock _l(mLock); - // Copy old handles for release if they are no longer present. - const std::vector> oldWindowHandles = - getWindowHandlesLocked(displayId); + // Copy old handles for release if they are no longer present. + const std::vector> oldWindowHandles = getWindowHandlesLocked(displayId); - updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); - sp newFocusedWindowHandle = nullptr; - bool foundHoveredWindow = false; - for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } + sp newFocusedWindowHandle = nullptr; + bool foundHoveredWindow = false; + for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; } - - if (!foundHoveredWindow) { - mLastHoverWindowHandle = nullptr; + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; } + } - sp oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (!foundHoveredWindow) { + mLastHoverWindowHandle = nullptr; + } - if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { - if (oldFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); - } - sp focusedInputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); - if (focusedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); - } - mFocusedWindowHandlesByDisplay.erase(displayId); + sp oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { + if (oldFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); } - if (newFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); - } - mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; - enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); + sp focusedInputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (focusedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } - - if (mFocusedDisplayId == displayId) { - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + mFocusedWindowHandlesByDisplay.erase(displayId); + } + if (newFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); } + mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex >= 0) { - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - for (size_t i = 0; i < state.windows.size();) { - TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { - if (DEBUG_FOCUS) { - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); - } - sp touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, - options); - } - state.windows.erase(state.windows.begin() + i); - } else { - ++i; - } - } + if (mFocusedDisplayId == displayId) { + onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); } + } - // Release information for windows that are no longer present. - // This ensures that unused input channels are released promptly. - // Otherwise, they might stick around until the window handle is destroyed - // which might not happen until the next GC. - for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex >= 0) { + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + for (size_t i = 0; i < state.windows.size();) { + TouchedWindow& touchedWindow = state.windows[i]; + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { if (DEBUG_FOCUS) { - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); + } + sp touchedInputChannel = + getInputChannelLocked(touchedWindow.windowHandle->getToken()); + if (touchedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); } - oldWindowHandle->releaseChannel(); + state.windows.erase(state.windows.begin() + i); + } else { + ++i; } } - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); + } - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); + // Release information for windows that are no longer present. + // This ensures that unused input channels are released promptly. + // Otherwise, they might stick around until the window handle is destroyed + // which might not happen until the next GC. + for (const sp& oldWindowHandle : oldWindowHandles) { + if (!hasWindowHandleLocked(oldWindowHandle)) { + if (DEBUG_FOCUS) { + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + } + oldWindowHandle->releaseChannel(); + } } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 4aa47f89f0..cbba7e1318 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -109,8 +109,8 @@ public: virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; virtual void setInputWindows( - const std::vector>& inputWindowHandles, int32_t displayId, - const sp& setInputWindowsListener = nullptr) override; + const std::unordered_map>>& + handlesPerDisplay) override; virtual void setFocusedApplication( int32_t displayId, const sp& inputApplicationHandle) override; virtual void setFocusedDisplay(int32_t displayId) override; @@ -278,6 +278,8 @@ private: std::unordered_map>> mWindowHandlesByDisplay GUARDED_BY(mLock); + void setInputWindowsLocked(const std::vector>& inputWindowHandles, + int32_t displayId) REQUIRES(mLock); // Get window handles by display, return an empty vector if not found. std::vector> getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 6e986768fc..09dc92c8fa 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -19,6 +19,7 @@ #include #include +#include namespace android { @@ -99,13 +100,13 @@ public: */ virtual std::unique_ptr verifyInputEvent(const InputEvent& event) = 0; - /* Sets the list of input windows. + /* Sets the list of input windows per display. * * This method may be called on any thread (usually by the input manager). */ virtual void setInputWindows( - const std::vector >& inputWindowHandles, int32_t displayId, - const sp& setInputWindowsListener = nullptr) = 0; + const std::unordered_map>>& + handlesPerDisplay) = 0; /* Sets the focused application on the given display. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 1f283b18ac..29f3dac03e 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -871,7 +871,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -888,7 +888,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { sp windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -910,7 +910,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Display should have only one focused window windowSecond->setFocus(true); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowSecond->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -935,7 +935,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { windowTop->setFocus(true); windowSecond->setFocus(true); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowTop->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -960,7 +960,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->setFocus(true); // Release channel for window is no longer valid. windowTop->releaseChannel(); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowSecond->consumeFocusEvent(true); // Test inject a key down, should dispatch to a valid window. @@ -986,7 +986,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}}); // Inject an event with coordinate in the area of right window, with mouse cursor in the area of // left window. This event should be dispatched to the left window. @@ -1003,7 +1003,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1025,7 +1025,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1053,7 +1053,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1090,7 +1090,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1152,7 +1152,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { | InputWindowInfo::FLAG_SPLIT_TOUCH); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); PointF pointInFirst = {300, 200}; PointF pointInSecond = {300, 600}; @@ -1204,7 +1204,7 @@ TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); @@ -1220,7 +1220,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyKey(&keyArgs); @@ -1235,7 +1235,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); // Send key NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1289,7 +1289,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1309,7 +1309,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, @@ -1325,7 +1325,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1351,7 +1351,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1387,29 +1387,29 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->setFocus(true); SCOPED_TRACE("Check default value of touch mode"); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Disable touch mode"); mDispatcher->setInTouchMode(false); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Enable touch mode again"); mDispatcher->setInTouchMode(true); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); window->assertNoEvents(); @@ -1423,7 +1423,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); @@ -1459,7 +1459,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1512,7 +1512,7 @@ protected: mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mWindow->setFocus(true); - mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); mWindow->consumeFocusEvent(true); } @@ -1602,7 +1602,7 @@ public: // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); windowInPrimary->setFocus(true); - mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}}); windowInPrimary->consumeFocusEvent(true); application2 = new FakeApplicationHandle(); @@ -1614,7 +1614,7 @@ public: // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocus(true); - mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}}); windowInSecondary->consumeFocusEvent(true); } @@ -1664,7 +1664,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); // Remove all windows in secondary display. - mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID); + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); // Expect old focus should receive a cancel event. windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, @@ -1828,7 +1828,7 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { mFocusedWindow->setFocus(true); // Expect one focus window exist in display. - mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); mFocusedWindow->consumeFocusEvent(true); } @@ -1916,7 +1916,7 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); - mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); } protected: -- cgit v1.2.3-59-g8ed1b From c60da19f2a012a15971dcd8f4fa3b7c22a90a670 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 19 Mar 2020 11:55:01 -0700 Subject: Disable deep press when long press timeout is long If the user sets the long press timeout to anything > default value, then we should disable deep press. That means, if the user is triggering long press too easily, it is likely that the user will trigger deep press too easily as well. To prevent unwanted long press invocations, we disable deep press in such situations. Bug: 148311342 Test: Add logging to both cases where deep press is enabled and disabled. Go to accessibility -> touch & hold delay, and change the value. Ensure that the logging matches expectation (short -> enable, medium/long -> disable). The state should persist across the reboot. Change-Id: Ic631c684609c4436e9eaa6f9389939c5a6e4e1cd Merged-In: Ic631c684609c4436e9eaa6f9389939c5a6e4e1cd (cherry picked from commit c9ac19eb3d3c3f185fea95906541249dd4c3baf8) --- services/inputflinger/Android.bp | 1 - services/inputflinger/InputClassifier.cpp | 55 ++++++++-------------- services/inputflinger/InputClassifier.h | 4 ++ services/inputflinger/InputManager.cpp | 4 ++ services/inputflinger/InputManager.h | 2 + .../inputflinger/tests/InputClassifier_test.cpp | 22 +++++++++ 6 files changed, 52 insertions(+), 36 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 4ec4e25010..f67c9d006b 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -52,7 +52,6 @@ cc_defaults { "libstatslog", "libutils", "libui", - "server_configurable_flags", ], } diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 8ba1f7f8c1..77a0716269 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -27,7 +27,6 @@ #if defined(__linux__) #include #endif -#include #include #include @@ -46,11 +45,6 @@ using namespace android::hardware::input; namespace android { -// Category (=namespace) name for the input settings that are applied at boot time -static const char* INPUT_NATIVE_BOOT = "input_native_boot"; -// Feature flag name for the deep press feature -static const char* DEEP_PRESS_ENABLED = "deep_press_enabled"; - //Max number of elements to store in mEvents. static constexpr size_t MAX_EVENTS = 5; @@ -77,20 +71,6 @@ static bool isTouchEvent(const NotifyMotionArgs& args) { return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN; } -// Check if the "deep touch" feature is on. -static bool deepPressEnabled() { - std::string flag_value = server_configurable_flags::GetServerConfigurableFlag( - INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true"); - std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower); - if (flag_value == "1" || flag_value == "true") { - ALOGI("Deep press feature enabled."); - return true; - } - ALOGI("Deep press feature is not enabled."); - return false; -} - - // --- ClassifierEvent --- ClassifierEvent::ClassifierEvent(std::unique_ptr args) : @@ -157,12 +137,6 @@ MotionClassifier::MotionClassifier( std::unique_ptr MotionClassifier::create( sp deathRecipient) { - if (!deepPressEnabled()) { - // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. - // When MotionClassifier is null, InputClassifier will forward all events - // to the next InputListener, unmodified. - return nullptr; - } sp service = classifier::V1_0::IInputClassifier::getService(); if (!service) { @@ -372,14 +346,25 @@ void InputClassifier::HalDeathRecipient::serviceDied( // --- InputClassifier --- InputClassifier::InputClassifier(const sp& listener) - : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) { - mInitializeMotionClassifierThread = std::thread( - [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); + : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {} + +void InputClassifier::setMotionClassifierEnabled(bool enabled) { + if (enabled) { + ALOGI("Enabling motion classifier"); + if (mInitializeMotionClassifierThread.joinable()) { + mInitializeMotionClassifierThread.join(); + } + mInitializeMotionClassifierThread = std::thread( + [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); #if defined(__linux__) - // Set the thread name for debugging - pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), - "Create MotionClassifier"); + // Set the thread name for debugging + pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), + "Create MotionClassifier"); #endif + } else { + ALOGI("Disabling motion classifier"); + setMotionClassifier(nullptr); + } } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { @@ -429,8 +414,6 @@ void InputClassifier::setMotionClassifier( void InputClassifier::dump(std::string& dump) { std::scoped_lock lock(mLock); dump += "Input Classifier State:\n"; - dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled"); - dump += INDENT1 "Motion Classifier:\n"; if (mMotionClassifier) { mMotionClassifier->dump(dump); @@ -441,7 +424,9 @@ void InputClassifier::dump(std::string& dump) { } InputClassifier::~InputClassifier() { - mInitializeMotionClassifierThread.join(); + if (mInitializeMotionClassifierThread.joinable()) { + mInitializeMotionClassifierThread.join(); + } } } // namespace android diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 8f586956e5..03510a623c 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -90,6 +90,7 @@ public: */ class InputClassifierInterface : public virtual RefBase, public InputListenerInterface { public: + virtual void setMotionClassifierEnabled(bool enabled) = 0; /** * Dump the state of the input classifier. * This method may be called on any thread (usually by the input manager). @@ -234,6 +235,9 @@ public: ~InputClassifier(); + // Called from InputManager + virtual void setMotionClassifierEnabled(bool enabled) override; + private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index c7c61cf1ef..fc771a2c58 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -132,4 +132,8 @@ void InputManager::unregisterInputChannel(const sp& channel) { mDispatcher->unregisterInputChannel(channel); } +void InputManager::setMotionClassifierEnabled(bool enabled) { + mClassifier->setMotionClassifierEnabled(enabled); +} + } // namespace android diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 586097f6a3..0158441fd1 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -100,6 +100,8 @@ public: virtual void registerInputChannel(const sp& channel); virtual void unregisterInputChannel(const sp& channel); + void setMotionClassifierEnabled(bool enabled); + private: sp mReader; diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index b4e755a595..ab74a0498d 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -135,6 +135,28 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { ASSERT_EQ(args, outArgs); } +TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) { + mClassifier->setMotionClassifierEnabled(true); +} + +TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) { + mClassifier->setMotionClassifierEnabled(false); +} + +/** + * Try to break it by calling setMotionClassifierEnabled multiple times. + */ +TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) { + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(false); + mClassifier->setMotionClassifierEnabled(false); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); +} + /** * A minimal implementation of IInputClassifier. */ -- cgit v1.2.3-59-g8ed1b From 25e2af10b9200f2970985b54db17b6d427928e89 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 26 Mar 2020 12:58:37 +0000 Subject: Revert "Fix input infos are inconsistent between WMS and InputFlinger (2/2)" This reverts commit 2d112c5f625d09bb703b8909289d11f02ee44398. Reason for revert: Inject event may take too long if no animation. Change-Id: Ie101f881363cf088a73a8b106fd242c15c7788bf --- services/inputflinger/InputManager.cpp | 6 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 173 +++++++++++---------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../dispatcher/include/InputDispatcherInterface.h | 7 +- .../inputflinger/tests/InputDispatcher_test.cpp | 62 ++++---- 6 files changed, 127 insertions(+), 131 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index f2a0014da4..c7c61cf1ef 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -111,10 +111,8 @@ void InputManager::setInputWindows(const std::vector& infos, handlesPerDisplay.emplace(info.displayId, std::vector>()); handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); } - mDispatcher->setInputWindows(handlesPerDisplay); - - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); + for (auto const& i : handlesPerDisplay) { + mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener); } } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 7c5c9c5f0c..3b18813cef 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -252,7 +252,7 @@ static void benchmarkNotifyMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); NotifyMotionArgs motionArgs = generateMotionArgs(); @@ -288,7 +288,7 @@ static void benchmarkInjectMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); for (auto _ : state) { MotionEvent event = generateMotionEvent(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ec61b0c63..308d19b085 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3621,18 +3621,6 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( mWindowHandlesByDisplay[displayId] = newHandles; } -void InputDispatcher::setInputWindows( - const std::unordered_map>>& handlesPerDisplay) { - { // acquire lock - std::scoped_lock _l(mLock); - for (auto const& i : handlesPerDisplay) { - setInputWindowsLocked(i.second, i.first); - } - } - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); -} - /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... @@ -3640,8 +3628,9 @@ void InputDispatcher::setInputWindows( * For focused handle, check if need to change and send a cancel event to previous one. * For removed handle, check if need to send a cancel event if already in touch. */ -void InputDispatcher::setInputWindowsLocked( - const std::vector>& inputWindowHandles, int32_t displayId) { +void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, + int32_t displayId, + const sp& setInputWindowsListener) { if (DEBUG_FOCUS) { std::string windowList; for (const sp& iwh : inputWindowHandles) { @@ -3649,97 +3638,109 @@ void InputDispatcher::setInputWindowsLocked( } ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } + { // acquire lock + std::scoped_lock _l(mLock); - // Copy old handles for release if they are no longer present. - const std::vector> oldWindowHandles = getWindowHandlesLocked(displayId); + // Copy old handles for release if they are no longer present. + const std::vector> oldWindowHandles = + getWindowHandlesLocked(displayId); - updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); - sp newFocusedWindowHandle = nullptr; - bool foundHoveredWindow = false; - for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; + sp newFocusedWindowHandle = nullptr; + bool foundHoveredWindow = false; + for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; + } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; + } } - } - if (!foundHoveredWindow) { - mLastHoverWindowHandle = nullptr; - } + if (!foundHoveredWindow) { + mLastHoverWindowHandle = nullptr; + } - sp oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + sp oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { - if (oldFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { + if (oldFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); + } + sp focusedInputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (focusedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); + } + mFocusedWindowHandlesByDisplay.erase(displayId); } - sp focusedInputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); - if (focusedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); + if (newFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); + } + mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } - mFocusedWindowHandlesByDisplay.erase(displayId); - } - if (newFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); + + if (mFocusedDisplayId == displayId) { + onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); } - mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; - enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } - if (mFocusedDisplayId == displayId) { - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex >= 0) { + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + for (size_t i = 0; i < state.windows.size();) { + TouchedWindow& touchedWindow = state.windows[i]; + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { + if (DEBUG_FOCUS) { + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); + } + sp touchedInputChannel = + getInputChannelLocked(touchedWindow.windowHandle->getToken()); + if (touchedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, + options); + } + state.windows.erase(state.windows.begin() + i); + } else { + ++i; + } + } } - } - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex >= 0) { - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - for (size_t i = 0; i < state.windows.size();) { - TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { + // Release information for windows that are no longer present. + // This ensures that unused input channels are released promptly. + // Otherwise, they might stick around until the window handle is destroyed + // which might not happen until the next GC. + for (const sp& oldWindowHandle : oldWindowHandles) { + if (!hasWindowHandleLocked(oldWindowHandle)) { if (DEBUG_FOCUS) { - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); - } - sp touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); } - state.windows.erase(state.windows.begin() + i); - } else { - ++i; + oldWindowHandle->releaseChannel(); } } - } + } // release lock - // Release information for windows that are no longer present. - // This ensures that unused input channels are released promptly. - // Otherwise, they might stick around until the window handle is destroyed - // which might not happen until the next GC. - for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { - if (DEBUG_FOCUS) { - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); - } - oldWindowHandle->releaseChannel(); - } + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); + + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index cbba7e1318..4aa47f89f0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -109,8 +109,8 @@ public: virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; virtual void setInputWindows( - const std::unordered_map>>& - handlesPerDisplay) override; + const std::vector>& inputWindowHandles, int32_t displayId, + const sp& setInputWindowsListener = nullptr) override; virtual void setFocusedApplication( int32_t displayId, const sp& inputApplicationHandle) override; virtual void setFocusedDisplay(int32_t displayId) override; @@ -278,8 +278,6 @@ private: std::unordered_map>> mWindowHandlesByDisplay GUARDED_BY(mLock); - void setInputWindowsLocked(const std::vector>& inputWindowHandles, - int32_t displayId) REQUIRES(mLock); // Get window handles by display, return an empty vector if not found. std::vector> getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 09dc92c8fa..6e986768fc 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -19,7 +19,6 @@ #include #include -#include namespace android { @@ -100,13 +99,13 @@ public: */ virtual std::unique_ptr verifyInputEvent(const InputEvent& event) = 0; - /* Sets the list of input windows per display. + /* Sets the list of input windows. * * This method may be called on any thread (usually by the input manager). */ virtual void setInputWindows( - const std::unordered_map>>& - handlesPerDisplay) = 0; + const std::vector >& inputWindowHandles, int32_t displayId, + const sp& setInputWindowsListener = nullptr) = 0; /* Sets the focused application on the given display. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 29f3dac03e..1f283b18ac 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -871,7 +871,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -888,7 +888,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { sp windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -910,7 +910,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Display should have only one focused window windowSecond->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); windowSecond->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -935,7 +935,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { windowTop->setFocus(true); windowSecond->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); windowTop->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -960,7 +960,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->setFocus(true); // Release channel for window is no longer valid. windowTop->releaseChannel(); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); windowSecond->consumeFocusEvent(true); // Test inject a key down, should dispatch to a valid window. @@ -986,7 +986,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}}); + mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT); // Inject an event with coordinate in the area of right window, with mouse cursor in the area of // left window. This event should be dispatched to the left window. @@ -1003,7 +1003,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1025,7 +1025,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1053,7 +1053,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1090,7 +1090,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1152,7 +1152,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { | InputWindowInfo::FLAG_SPLIT_TOUCH); // Add the windows to the dispatcher - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); PointF pointInFirst = {300, 200}; PointF pointInSecond = {300, 600}; @@ -1204,7 +1204,7 @@ TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true); @@ -1220,7 +1220,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyKey(&keyArgs); @@ -1235,7 +1235,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); // Send key NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1289,7 +1289,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1309,7 +1309,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, @@ -1325,7 +1325,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1351,7 +1351,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1387,29 +1387,29 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->setFocus(true); SCOPED_TRACE("Check default value of touch mode"); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Disable touch mode"); mDispatcher->setInTouchMode(false); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Enable touch mode again"); mDispatcher->setInTouchMode(true); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); window->assertNoEvents(); @@ -1423,7 +1423,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); @@ -1459,7 +1459,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1512,7 +1512,7 @@ protected: mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mWindow->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT); mWindow->consumeFocusEvent(true); } @@ -1602,7 +1602,7 @@ public: // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); windowInPrimary->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}}); + mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); windowInPrimary->consumeFocusEvent(true); application2 = new FakeApplicationHandle(); @@ -1614,7 +1614,7 @@ public: // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocus(true); - mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}}); + mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); windowInSecondary->consumeFocusEvent(true); } @@ -1664,7 +1664,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); // Remove all windows in secondary display. - mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); + mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID); // Expect old focus should receive a cancel event. windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, @@ -1828,7 +1828,7 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { mFocusedWindow->setFocus(true); // Expect one focus window exist in display. - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); mFocusedWindow->consumeFocusEvent(true); } @@ -1916,7 +1916,7 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); + mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); } protected: -- cgit v1.2.3-59-g8ed1b From 72d8dc39f1a58491c9fb4495079baec6d5c4b41e Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Sat, 28 Mar 2020 00:48:39 +0000 Subject: Fix input infos are inconsistent between WMS and InputFlinger (2/2) In single focus system, the top focused display may change to the new one when receive a key event. And we would expect the focus could be changed before the event enqueued into inbound queue. This patch refactor 'setInputWindows' to prevent early callback and wakeup in first display, and make sure 'syncInputWindow' could wait until all input windows from all displays updated. Bug: 150250453 Test: atest libinput_tests inputflinger_tests Test: atest --rerun-until-failure 100 WindowFocusTests#testMovingDisplayToTopByKeyEvent Change-Id: I60295975a833df330005943469233c158dd2b07b --- services/inputflinger/InputManager.cpp | 6 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 173 ++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../dispatcher/include/InputDispatcherInterface.h | 7 +- .../inputflinger/tests/InputDispatcher_test.cpp | 62 ++++---- 6 files changed, 131 insertions(+), 127 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index fc771a2c58..e68946d734 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -111,8 +111,10 @@ void InputManager::setInputWindows(const std::vector& infos, handlesPerDisplay.emplace(info.displayId, std::vector>()); handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); } - for (auto const& i : handlesPerDisplay) { - mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener); + mDispatcher->setInputWindows(handlesPerDisplay); + + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); } } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 3b18813cef..7c5c9c5f0c 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -252,7 +252,7 @@ static void benchmarkNotifyMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(); @@ -288,7 +288,7 @@ static void benchmarkInjectMotion(benchmark::State& state) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); - dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); for (auto _ : state) { MotionEvent event = generateMotionEvent(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 308d19b085..4ec61b0c63 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3621,6 +3621,18 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( mWindowHandlesByDisplay[displayId] = newHandles; } +void InputDispatcher::setInputWindows( + const std::unordered_map>>& handlesPerDisplay) { + { // acquire lock + std::scoped_lock _l(mLock); + for (auto const& i : handlesPerDisplay) { + setInputWindowsLocked(i.second, i.first); + } + } + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... @@ -3628,9 +3640,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( * For focused handle, check if need to change and send a cancel event to previous one. * For removed handle, check if need to send a cancel event if already in touch. */ -void InputDispatcher::setInputWindows(const std::vector>& inputWindowHandles, - int32_t displayId, - const sp& setInputWindowsListener) { +void InputDispatcher::setInputWindowsLocked( + const std::vector>& inputWindowHandles, int32_t displayId) { if (DEBUG_FOCUS) { std::string windowList; for (const sp& iwh : inputWindowHandles) { @@ -3638,109 +3649,97 @@ void InputDispatcher::setInputWindows(const std::vector>& } ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } - { // acquire lock - std::scoped_lock _l(mLock); - // Copy old handles for release if they are no longer present. - const std::vector> oldWindowHandles = - getWindowHandlesLocked(displayId); + // Copy old handles for release if they are no longer present. + const std::vector> oldWindowHandles = getWindowHandlesLocked(displayId); - updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); - sp newFocusedWindowHandle = nullptr; - bool foundHoveredWindow = false; - for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } + sp newFocusedWindowHandle = nullptr; + bool foundHoveredWindow = false; + for (const sp& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; } - - if (!foundHoveredWindow) { - mLastHoverWindowHandle = nullptr; + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; } + } - sp oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (!foundHoveredWindow) { + mLastHoverWindowHandle = nullptr; + } - if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { - if (oldFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); - } - sp focusedInputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); - if (focusedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); - } - mFocusedWindowHandlesByDisplay.erase(displayId); + sp oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + + if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { + if (oldFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); } - if (newFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); - } - mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; - enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); + sp focusedInputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (focusedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } - - if (mFocusedDisplayId == displayId) { - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + mFocusedWindowHandlesByDisplay.erase(displayId); + } + if (newFocusedWindowHandle != nullptr) { + if (DEBUG_FOCUS) { + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); } + mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex >= 0) { - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - for (size_t i = 0; i < state.windows.size();) { - TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { - if (DEBUG_FOCUS) { - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); - } - sp touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, - options); - } - state.windows.erase(state.windows.begin() + i); - } else { - ++i; - } - } + if (mFocusedDisplayId == displayId) { + onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); } + } - // Release information for windows that are no longer present. - // This ensures that unused input channels are released promptly. - // Otherwise, they might stick around until the window handle is destroyed - // which might not happen until the next GC. - for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex >= 0) { + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + for (size_t i = 0; i < state.windows.size();) { + TouchedWindow& touchedWindow = state.windows[i]; + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { if (DEBUG_FOCUS) { - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); + } + sp touchedInputChannel = + getInputChannelLocked(touchedWindow.windowHandle->getToken()); + if (touchedInputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); } - oldWindowHandle->releaseChannel(); + state.windows.erase(state.windows.begin() + i); + } else { + ++i; } } - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); + } - if (setInputWindowsListener) { - setInputWindowsListener->onSetInputWindowsFinished(); + // Release information for windows that are no longer present. + // This ensures that unused input channels are released promptly. + // Otherwise, they might stick around until the window handle is destroyed + // which might not happen until the next GC. + for (const sp& oldWindowHandle : oldWindowHandles) { + if (!hasWindowHandleLocked(oldWindowHandle)) { + if (DEBUG_FOCUS) { + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + } + oldWindowHandle->releaseChannel(); + } } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 4aa47f89f0..cbba7e1318 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -109,8 +109,8 @@ public: virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; virtual void setInputWindows( - const std::vector>& inputWindowHandles, int32_t displayId, - const sp& setInputWindowsListener = nullptr) override; + const std::unordered_map>>& + handlesPerDisplay) override; virtual void setFocusedApplication( int32_t displayId, const sp& inputApplicationHandle) override; virtual void setFocusedDisplay(int32_t displayId) override; @@ -278,6 +278,8 @@ private: std::unordered_map>> mWindowHandlesByDisplay GUARDED_BY(mLock); + void setInputWindowsLocked(const std::vector>& inputWindowHandles, + int32_t displayId) REQUIRES(mLock); // Get window handles by display, return an empty vector if not found. std::vector> getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 6e986768fc..09dc92c8fa 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -19,6 +19,7 @@ #include #include +#include namespace android { @@ -99,13 +100,13 @@ public: */ virtual std::unique_ptr verifyInputEvent(const InputEvent& event) = 0; - /* Sets the list of input windows. + /* Sets the list of input windows per display. * * This method may be called on any thread (usually by the input manager). */ virtual void setInputWindows( - const std::vector >& inputWindowHandles, int32_t displayId, - const sp& setInputWindowsListener = nullptr) = 0; + const std::unordered_map>>& + handlesPerDisplay) = 0; /* Sets the focused application on the given display. * diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 1f283b18ac..29f3dac03e 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -871,7 +871,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -888,7 +888,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { sp windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -910,7 +910,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Display should have only one focused window windowSecond->setFocus(true); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowSecond->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -935,7 +935,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { windowTop->setFocus(true); windowSecond->setFocus(true); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowTop->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -960,7 +960,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->setFocus(true); // Release channel for window is no longer valid. windowTop->releaseChannel(); - mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); windowSecond->consumeFocusEvent(true); // Test inject a key down, should dispatch to a valid window. @@ -986,7 +986,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}}); // Inject an event with coordinate in the area of right window, with mouse cursor in the area of // left window. This event should be dispatched to the left window. @@ -1003,7 +1003,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1025,7 +1025,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1053,7 +1053,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1090,7 +1090,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { "Second Window", ADISPLAY_ID_DEFAULT); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -1152,7 +1152,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { | InputWindowInfo::FLAG_SPLIT_TOUCH); // Add the windows to the dispatcher - mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); PointF pointInFirst = {300, 200}; PointF pointInSecond = {300, 600}; @@ -1204,7 +1204,7 @@ TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); @@ -1220,7 +1220,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyKey(&keyArgs); @@ -1235,7 +1235,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); // Send key NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1289,7 +1289,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1309,7 +1309,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, @@ -1325,7 +1325,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea sp application = new FakeApplicationHandle(); sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1351,7 +1351,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { sp window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1387,29 +1387,29 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->setFocus(true); SCOPED_TRACE("Check default value of touch mode"); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Disable touch mode"); mDispatcher->setInTouchMode(false); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); window->setFocus(false); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Enable touch mode again"); mDispatcher->setInTouchMode(true); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); window->assertNoEvents(); @@ -1423,7 +1423,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocus(true); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); @@ -1459,7 +1459,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -1512,7 +1512,7 @@ protected: mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mWindow->setFocus(true); - mDispatcher->setInputWindows({mWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); mWindow->consumeFocusEvent(true); } @@ -1602,7 +1602,7 @@ public: // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); windowInPrimary->setFocus(true); - mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}}); windowInPrimary->consumeFocusEvent(true); application2 = new FakeApplicationHandle(); @@ -1614,7 +1614,7 @@ public: // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocus(true); - mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}}); windowInSecondary->consumeFocusEvent(true); } @@ -1664,7 +1664,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); // Remove all windows in secondary display. - mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID); + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); // Expect old focus should receive a cancel event. windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, @@ -1828,7 +1828,7 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { mFocusedWindow->setFocus(true); // Expect one focus window exist in display. - mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); mFocusedWindow->consumeFocusEvent(true); } @@ -1916,7 +1916,7 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); - mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); } protected: -- cgit v1.2.3-59-g8ed1b From 4197f6b96f55d3c9d6fe2365be4c4ffcb49e9a7e Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Mon, 16 Mar 2020 15:39:59 +0800 Subject: Fix tap is wrong in landscape with cutout disabled The coordinate values in TouchInputMapper and raw events should be orientation-independent, we should not rotate the raw events while checking the range. And it should start from the current logcial right/bottom instead of the original right/bottom when it needs to reverse the coordinate. - Let 'rotateAndScale' could transform raw coordinate to surface. - Range checking should only base on natural orientation. - Change the test case to be more robust and could test 90 and 270 degrees cases. Bug: 138708005 Test: atest inputflinger_tests Test: open foldable emulator, change to minimized state and rotate device, test touch. Test: let device has display offset, rotate device and check touch. Change-Id: I945e489866bc41b5c14ed4a143952bc85248b3e9 --- .../reader/mapper/TouchInputMapper.cpp | 106 +++++++------ .../inputflinger/reader/mapper/TouchInputMapper.h | 8 +- services/inputflinger/tests/InputReader_test.cpp | 170 +++++++++++++++------ 3 files changed, 189 insertions(+), 95 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index bbc8e537c6..99a572a5fd 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -160,8 +160,8 @@ TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext), mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceWidth(-1), - mSurfaceHeight(-1), + mRawSurfaceWidth(-1), + mRawSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), mPhysicalWidth(-1), @@ -680,7 +680,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - naturalPhysicalWidth; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; naturalPhysicalTop = mViewport.physicalLeft; naturalDeviceWidth = mViewport.deviceHeight; naturalDeviceHeight = mViewport.deviceWidth; @@ -701,7 +701,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - naturalPhysicalHeight; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; naturalDeviceWidth = mViewport.deviceHeight; naturalDeviceHeight = mViewport.deviceWidth; break; @@ -729,10 +729,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPhysicalLeft = naturalPhysicalLeft; mPhysicalTop = naturalPhysicalTop; - mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + mSurfaceRight = mSurfaceLeft + naturalLogicalWidth; + mSurfaceBottom = mSurfaceTop + naturalLogicalHeight; mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; @@ -742,8 +744,8 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPhysicalLeft = 0; mPhysicalTop = 0; - mSurfaceWidth = rawWidth; - mSurfaceHeight = rawHeight; + mRawSurfaceWidth = rawWidth; + mRawSurfaceHeight = rawHeight; mSurfaceLeft = 0; mSurfaceTop = 0; mSurfaceOrientation = DISPLAY_ORIENTATION_0; @@ -769,12 +771,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { if (viewportChanged || deviceModeChanged) { ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " "display id %d", - getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, + getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight, mSurfaceOrientation, mDeviceMode, mViewport.displayId); // Configure X and Y factors. - mXScale = float(mSurfaceWidth) / rawWidth; - mYScale = float(mSurfaceHeight) / rawHeight; + mXScale = float(mRawSurfaceWidth) / rawWidth; + mYScale = float(mRawSurfaceHeight) / rawHeight; mXTranslate = -mSurfaceLeft; mYTranslate = -mSurfaceTop; mXPrecision = 1.0f / mXScale; @@ -793,7 +795,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mGeometricScale = avg(mXScale, mYScale); // Size of diagonal axis. - float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); + float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight); // Size factors. if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { @@ -956,13 +958,13 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedYPrecision = mXPrecision; mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; @@ -973,13 +975,13 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedYPrecision = mYPrecision; mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; @@ -992,7 +994,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { if (mDeviceMode == DEVICE_MODE_POINTER) { // Compute pointer gesture detection parameters. float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); + float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight); // Scale movements such that one whole swipe of the touch pad covers a // given area relative to the diagonal size of the display when no acceleration @@ -1027,10 +1029,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { void TouchInputMapper::dumpSurface(std::string& dump) { dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); - dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth); + dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight); dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight); + dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom); dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); @@ -1074,16 +1078,16 @@ void TouchInputMapper::configureVirtualKeys() { int32_t halfHeight = virtualKeyDefinition.height / 2; virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth + touchScreenLeft; virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth + touchScreenLeft; - virtualKey.hitTop = - (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / + mRawSurfaceHeight + touchScreenTop; - virtualKey.hitBottom = - (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / + mRawSurfaceHeight + touchScreenTop; mVirtualKeys.push_back(virtualKey); } @@ -2188,13 +2192,10 @@ void TouchInputMapper::cookPointerData() { rotateAndScale(xTransformed, yTransformed); // Adjust X, Y, and coverage coords for surface orientation. - float x, y; float left, top, right, bottom; switch (mSurfaceOrientation) { case DISPLAY_ORIENTATION_90: - x = yTransformed + mYTranslate; - y = xTransformed + mXTranslate; left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; @@ -2207,8 +2208,6 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_180: - x = xTransformed + mXTranslate; - y = yTransformed + mYTranslate; left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; @@ -2221,8 +2220,6 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_270: - x = yTransformed + mYTranslate; - y = xTransformed + mXTranslate; left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; @@ -2235,8 +2232,6 @@ void TouchInputMapper::cookPointerData() { } break; default: - x = xTransformed + mXTranslate; - y = yTransformed + mYTranslate; left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; @@ -2247,8 +2242,8 @@ void TouchInputMapper::cookPointerData() { // Write output coords. PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed); out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); @@ -3624,34 +3619,47 @@ void TouchInputMapper::cancelTouch(nsecs_t when) { abortTouches(when, 0 /* policyFlags*/); } +// Transform raw coordinate to surface coordinate void TouchInputMapper::rotateAndScale(float& x, float& y) { + // Scale to surface coordinate. + const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale; + const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale; + + // Rotate to surface coordinate. + // 0 - no swap and reverse. + // 90 - swap x/y and reverse y. + // 180 - reverse x, y. + // 270 - swap x/y and reverse x. switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_0: + x = xScaled + mXTranslate; + y = yScaled + mYTranslate; + break; case DISPLAY_ORIENTATION_90: - x = float(mRawPointerAxes.x.maxValue - x) * mXScale; - y = float(y - mRawPointerAxes.y.minValue) * mYScale; + y = mSurfaceRight - xScaled; + x = yScaled + mYTranslate; break; case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - x) * mXScale; - y = float(mRawPointerAxes.y.maxValue - y) * mYScale; + x = mSurfaceRight - xScaled; + y = mSurfaceBottom - yScaled; break; case DISPLAY_ORIENTATION_270: - x = float(x - mRawPointerAxes.x.minValue) * mXScale; - y = float(mRawPointerAxes.y.maxValue - y) * mYScale; + y = xScaled + mXTranslate; + x = mSurfaceBottom - yScaled; break; default: - x = float(x - mRawPointerAxes.x.minValue) * mXScale; - y = float(y - mRawPointerAxes.y.minValue) * mYScale; - break; + assert(false); } } bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - float xTransformed = x, yTransformed = y; - rotateAndScale(xTransformed, yTransformed); + const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale; + const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale; + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - xTransformed >= mSurfaceLeft && xTransformed <= mSurfaceLeft + mSurfaceWidth && + xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - yTransformed >= mSurfaceTop && yTransformed <= mSurfaceTop + mSurfaceHeight; + yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom; } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index e21a33abed..58bfc5c596 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -407,12 +407,16 @@ private: // The surface orientation, width and height set by configureSurface(). // The width and height are derived from the viewport but are specified // in the natural orientation. + // They could be used for calculating diagonal, scaling factors, and virtual keys. + int32_t mRawSurfaceWidth; + int32_t mRawSurfaceHeight; + // The surface origin specifies how the surface coordinates should be translated // to align with the logical display coordinate space. - int32_t mSurfaceWidth; - int32_t mSurfaceHeight; int32_t mSurfaceLeft; int32_t mSurfaceTop; + int32_t mSurfaceRight; + int32_t mSurfaceBottom; // Similar to the surface coordinates, but in the raw display coordinate space rather than in // the logical coordinate space. diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 618aefc741..96d86b68fc 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -6990,51 +6990,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId); } -/** - * Test touch should not work if outside of surface. - */ -TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - MultiTouchInputMapper& mapper = addMapperAndConfigure(); - - // Touch on left-top area should work. - int32_t rawX = DISPLAY_WIDTH / 2 - 1; - int32_t rawY = DISPLAY_HEIGHT / 2 - 1; - processPosition(mapper, rawX, rawY); - processSync(mapper); - - NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - - // Reset. - mapper.reset(ARBITRARY_TIME); - - // Let logical display be different to physical display and rotate 90-degrees. - std::optional internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - internalViewport->orientation = DISPLAY_ORIENTATION_90; - internalViewport->logicalLeft = 0; - internalViewport->logicalTop = 0; - internalViewport->logicalRight = DISPLAY_HEIGHT; - internalViewport->logicalBottom = DISPLAY_WIDTH / 2; - - internalViewport->physicalLeft = DISPLAY_HEIGHT; - internalViewport->physicalTop = DISPLAY_WIDTH / 2; - internalViewport->physicalRight = DISPLAY_HEIGHT; - internalViewport->physicalBottom = DISPLAY_WIDTH; - - internalViewport->deviceWidth = DISPLAY_HEIGHT; - internalViewport->deviceHeight = DISPLAY_WIDTH; - mFakePolicy->updateViewport(internalViewport.value()); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - // Display align to right-top after rotate 90-degrees, touch on left-top area should not work. - processPosition(mapper, rawX, rawY); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { addConfigurationProperty("touch.deviceType", "touchScreen"); @@ -7161,4 +7117,130 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); } + +/** + * Test touch should not work if outside of surface. + */ +class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest { +protected: + void halfDisplayToCenterHorizontal(int32_t orientation) { + std::optional internalViewport = + mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + + // Half display to (width/4, 0, width * 3/4, height) to make display has offset. + internalViewport->orientation = orientation; + if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) { + internalViewport->logicalLeft = 0; + internalViewport->logicalTop = 0; + internalViewport->logicalRight = DISPLAY_HEIGHT; + internalViewport->logicalBottom = DISPLAY_WIDTH / 2; + + internalViewport->physicalLeft = 0; + internalViewport->physicalTop = DISPLAY_WIDTH / 4; + internalViewport->physicalRight = DISPLAY_HEIGHT; + internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4; + + internalViewport->deviceWidth = DISPLAY_HEIGHT; + internalViewport->deviceHeight = DISPLAY_WIDTH; + } else { + internalViewport->logicalLeft = 0; + internalViewport->logicalTop = 0; + internalViewport->logicalRight = DISPLAY_WIDTH / 2; + internalViewport->logicalBottom = DISPLAY_HEIGHT; + + internalViewport->physicalLeft = DISPLAY_WIDTH / 4; + internalViewport->physicalTop = 0; + internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4; + internalViewport->physicalBottom = DISPLAY_HEIGHT; + + internalViewport->deviceWidth = DISPLAY_WIDTH; + internalViewport->deviceHeight = DISPLAY_HEIGHT; + } + + mFakePolicy->updateViewport(internalViewport.value()); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + } + + void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xInside, int32_t yInside, + int32_t xOutside, int32_t yOutside, int32_t xExpected, + int32_t yExpected) { + // touch on outside area should not work. + processPosition(mapper, toRawX(xOutside), toRawY(yOutside)); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // touch on inside area should receive the event. + NotifyMotionArgs args; + processPosition(mapper, toRawX(xInside), toRawY(yInside)); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + + // Reset. + mapper.reset(ARBITRARY_TIME); + } +}; + +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + // Touch on center of normal display should work. + const int32_t x = DISPLAY_WIDTH / 4; + const int32_t y = DISPLAY_HEIGHT / 2; + processPosition(mapper, toRawX(x), toRawY(y)); + processSync(mapper); + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f)); + // Reset. + mapper.reset(ARBITRARY_TIME); + + // Let physical display be different to device, and make surface and physical could be 1:1. + halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0); + + const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4); + const int32_t yExpected = y; + processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); +} + +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees. + halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90); + + const int32_t x = DISPLAY_WIDTH / 4; + const int32_t y = DISPLAY_HEIGHT / 2; + + // expect x/y = swap x/y then reverse y. + const int32_t xExpected = y; + const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1); + processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); +} + +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees. + halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270); + + const int32_t x = DISPLAY_WIDTH / 4; + const int32_t y = DISPLAY_HEIGHT / 2; + + // expect x/y = swap x/y then reverse x. + constexpr int32_t xExpected = DISPLAY_HEIGHT - y; + constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4; + processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); +} } // namespace android -- cgit v1.2.3-59-g8ed1b From e5b5e45ee1b0edecf836152ae3236ed218ee8401 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 2 Apr 2020 17:59:16 -0700 Subject: Wait longer for expected input events Sometimes, a device can be slow. That means, some events take a long time to process, especially if they involve kernel processing these from userspace and sending them back to userspace. When the input device is being added or removed, we are currently not waiting long enough to be notified. The test currently fails because the input device added notification never arrived. It turns out there are 2 cases where we are currently waiting for an input event from eventhub: 1) When we know how many events we expect, and we don't care how long they will take. We are willing to wait for a long time, because we know they will come eventually. When we got the expected number of events, we can stop waiting for more 2) When we have no idea how many events to expect, and we don't want to wait - we just need to take all immediately available. To account for the 2 (and only the 2 cases above), refactor EventHub_test to wait for a specific number of events instead of a timeout. If no expected number of events is provided, read all immediately available ones without a limit. This change also makes the test ~ 50% faster, because we are now stopping the wait after we have received the expected number of events. Bug: 149155998 Test: /data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter=*EventHubTest* --gtest_repeat=1000 Change-Id: I732c14c09567e9231bcc83141368b1614805f9ac --- services/inputflinger/tests/EventHub_test.cpp | 45 ++++++++++++++++----- services/inputflinger/tests/UinputDevice.cpp | 56 +++++++++++++-------------- 2 files changed, 63 insertions(+), 38 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index be2e19e7df..71731b046c 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -34,6 +34,7 @@ using android::RawEvent; using android::sp; using android::UinputHomeKey; using std::chrono_literals::operator""ms; +using std::chrono_literals::operator""s; static constexpr bool DEBUG = false; @@ -70,11 +71,12 @@ protected: mEventHub = std::make_unique(); consumeInitialDeviceAddedEvents(); mKeyboard = createUinputDevice(); - mDeviceId = waitForDeviceCreation(); + ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation()); } virtual void TearDown() override { mKeyboard.reset(); waitForDeviceClose(mDeviceId); + assertNoMoreEvents(); } /** @@ -83,21 +85,38 @@ protected: int32_t waitForDeviceCreation(); void waitForDeviceClose(int32_t deviceId); void consumeInitialDeviceAddedEvents(); - std::vector getEvents(std::chrono::milliseconds timeout = 5ms); + void assertNoMoreEvents(); + /** + * Read events from the EventHub. + * + * If expectedEvents is set, wait for a significant period of time to try and ensure that + * the expected number of events has been read. The number of returned events + * may be smaller (if timeout has been reached) or larger than expectedEvents. + * + * If expectedEvents is not set, return all of the immediately available events. + */ + std::vector getEvents(std::optional expectedEvents = std::nullopt); }; -std::vector EventHubTest::getEvents(std::chrono::milliseconds timeout) { +std::vector EventHubTest::getEvents(std::optional expectedEvents) { static constexpr size_t EVENT_BUFFER_SIZE = 256; std::array eventBuffer; std::vector events; while (true) { - size_t count = + std::chrono::milliseconds timeout = 0s; + if (expectedEvents) { + timeout = 2s; + } + const size_t count = mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size()); if (count == 0) { break; } events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count); + if (expectedEvents && events.size() >= *expectedEvents) { + break; + } } if (DEBUG) { dumpEvents(events); @@ -111,7 +130,7 @@ std::vector EventHubTest::getEvents(std::chrono::milliseconds timeout) * it will return a lot of "device added" type of events. */ void EventHubTest::consumeInitialDeviceAddedEvents() { - std::vector events = getEvents(0ms); + std::vector events = getEvents(); std::set existingDevices; // All of the events should be DEVICE_ADDED type, except the last one. for (size_t i = 0; i < events.size() - 1; i++) { @@ -128,8 +147,11 @@ void EventHubTest::consumeInitialDeviceAddedEvents() { int32_t EventHubTest::waitForDeviceCreation() { // Wait a little longer than usual, to ensure input device has time to be created - std::vector events = getEvents(20ms); - EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void. + std::vector events = getEvents(2); + if (events.size() != 2) { + ADD_FAILURE() << "Instead of 2 events, received " << events.size(); + return 0; // this value is unused + } const RawEvent& deviceAddedEvent = events[0]; EXPECT_EQ(static_cast(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type); InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId); @@ -142,7 +164,7 @@ int32_t EventHubTest::waitForDeviceCreation() { } void EventHubTest::waitForDeviceClose(int32_t deviceId) { - std::vector events = getEvents(20ms); + std::vector events = getEvents(2); ASSERT_EQ(2U, events.size()); const RawEvent& deviceRemovedEvent = events[0]; EXPECT_EQ(static_cast(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type); @@ -152,6 +174,11 @@ void EventHubTest::waitForDeviceClose(int32_t deviceId) { finishedDeviceScanEvent.type); } +void EventHubTest::assertNoMoreEvents() { + std::vector events = getEvents(); + ASSERT_TRUE(events.empty()); +} + /** * Ensure that input_events are generated with monotonic clock. * That means input_event should receive a timestamp that is in the future of the time @@ -162,7 +189,7 @@ TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) { nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC); ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey()); - std::vector events = getEvents(); + std::vector events = getEvents(4); ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events"; for (const RawEvent& event : events) { // Cannot use strict comparison because the events may happen too quickly diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 99480b71db..10e7293770 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -44,9 +44,7 @@ void UinputDevice::init() { device.id.product = 0x01; device.id.version = 1; - // Using EXPECT instead of ASSERT to allow the device creation to continue even when - // some failures are reported when configuring the device. - EXPECT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); + ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); if (write(mDeviceFd, &device, sizeof(device)) < 0) { FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: " @@ -70,7 +68,7 @@ void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { " with value %" PRId32 " : %s", type, code, value, strerror(errno)); ALOGE("%s", msg.c_str()); - ADD_FAILURE() << msg.c_str(); + FAIL() << msg.c_str(); } } @@ -82,41 +80,41 @@ UinputKeyboard::UinputKeyboard(std::initializer_list keys) void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { // enable key press/release event if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); + FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); } // enable set of KEY events std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) { if (ioctl(fd, UI_SET_KEYBIT, key)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno); + FAIL() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno); } }); // enable synchronization event if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) { - ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); + FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); } } void UinputKeyboard::pressKey(int key) { if (mKeys.find(key) == mKeys.end()) { - ADD_FAILURE() << mName << ": Cannot inject key press: Key not found: " << key; + FAIL() << mName << ": Cannot inject key press: Key not found: " << key; } - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 1)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_KEY, key, 1); + injectEvent(EV_SYN, SYN_REPORT, 0); } void UinputKeyboard::releaseKey(int key) { if (mKeys.find(key) == mKeys.end()) { - ADD_FAILURE() << mName << ": Cannot inject key release: Key not found: " << key; + FAIL() << mName << ": Cannot inject key release: Key not found: " << key; } - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 0)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_KEY, key, 0); + injectEvent(EV_SYN, SYN_REPORT, 0); } void UinputKeyboard::pressAndReleaseKey(int key) { - EXPECT_NO_FATAL_FAILURE(pressKey(key)); - EXPECT_NO_FATAL_FAILURE(releaseKey(key)); + pressKey(key); + releaseKey(key); } // --- UinputHomeKey --- @@ -124,7 +122,7 @@ void UinputKeyboard::pressAndReleaseKey(int key) { UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} void UinputHomeKey::pressAndReleaseHomeKey() { - EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME)); + pressAndReleaseKey(KEY_HOME); } // --- UinputTouchScreen --- @@ -158,35 +156,35 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { } void UinputTouchScreen::sendSlot(int32_t slot) { - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot)); + injectEvent(EV_ABS, ABS_MT_SLOT, slot); } void UinputTouchScreen::sendTrackingId(int32_t trackingId) { - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId)); + injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId); } void UinputTouchScreen::sendDown(const Point& point) { - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_KEY, BTN_TOUCH, 1); + injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x); + injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); + injectEvent(EV_SYN, SYN_REPORT, 0); } void UinputTouchScreen::sendMove(const Point& point) { - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x); + injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); + injectEvent(EV_SYN, SYN_REPORT, 0); } void UinputTouchScreen::sendUp() { sendTrackingId(0xffffffff); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_KEY, BTN_TOUCH, 0); + injectEvent(EV_SYN, SYN_REPORT, 0); } void UinputTouchScreen::sendToolType(int32_t toolType) { - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType)); - EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0)); + injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType); + injectEvent(EV_SYN, SYN_REPORT, 0); } // Get the center x, y base on the range definition. -- cgit v1.2.3-59-g8ed1b From 03aee2a35dc46f7e729153751936854545cb9bcd Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 13 Apr 2020 20:44:54 -0700 Subject: Consume events in dispatcher tests To prevent ANR in case the ANR logic gets refactored, consume the events that are being injected inside the dispatcher tests. Bug: 122535136 Test: atest inputflinger_tests Change-Id: I3362c61c26f1a2ef794790bef8ff96764912d781 --- services/inputflinger/tests/InputDispatcher_test.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 1f283b18ac..3f76dfd4ba 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -491,7 +491,7 @@ TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { // --- InputDispatcherTest SetInputWindowTest --- static constexpr int32_t INJECT_EVENT_TIMEOUT = 500; -static constexpr int32_t DISPATCHING_TIMEOUT = 100; +static constexpr nsecs_t DISPATCHING_TIMEOUT = seconds_to_nanoseconds(5); class FakeApplicationHandle : public InputApplicationHandle { public: @@ -1852,6 +1852,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Succe ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mUnfocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken()); @@ -1864,6 +1865,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertOnPointerDownWasNotCalled(); @@ -1874,6 +1876,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertOnPointerDownWasNotCalled(); @@ -1888,6 +1891,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mFocusedWindowTouchPoint, mFocusedWindowTouchPoint)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertOnPointerDownWasNotCalled(); -- cgit v1.2.3-59-g8ed1b From f0db5b8ca22824cd6ef75bf2ae7aebe58365d991 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 8 Apr 2020 19:22:14 -0700 Subject: Wait longer for configuration change Configuration changes can take > 50 ms. But currently, we are only waiting for 50 ms. If the cloud device is remote, it can be slow, so the current timeout is not sufficient. Extend the timeout for the cases where we expect the event to happen. At the same time, we don't need to wait for a long time to check that an event did not happen. Most of the time, the device is fast. On average, the event happens within 14 ms, with a standard deviation of 4.6 ms. Therefore, if we wait for 30 ms for an event to not happen, and the code is faulty, then the test is highly likely to fail. This would ensure that we don't encounter too many flakes, and the test will be mostly red if the code is faulty. Bug: 149155998 Test: tested on a remote cloud device (which is slow) Test: /data/nativetest/inputflinger_tests/inputflinger_tests --gtest_repeat=100 Change-Id: Ibd5ec86ef17ff6fac1d115a3fcc337fed7b20ae5 --- services/inputflinger/tests/InputReader_test.cpp | 3 ++- services/inputflinger/tests/TestInputListener.cpp | 17 ++++++++++------- services/inputflinger/tests/TestInputListener.h | 6 ++++-- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 618aefc741..3d26e75933 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1748,7 +1748,8 @@ protected: virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); - mTestListener = new TestInputListener(50ms); + mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/, + 30ms /*eventDidNotHappenTimeout*/); mReader = new InputReader(std::make_shared(), mFakePolicy, mTestListener); ASSERT_EQ(mReader->start(), OK); diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 86ff3b1d4d..9bff166565 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -23,7 +23,10 @@ namespace android { // --- TestInputListener --- -TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {} +TestInputListener::TestInputListener(std::chrono::milliseconds eventHappenedTimeout, + std::chrono::milliseconds eventDidNotHappenTimeout) + : mEventHappenedTimeout(eventHappenedTimeout), + mEventDidNotHappenTimeout(eventDidNotHappenTimeout) {} TestInputListener::~TestInputListener() { } @@ -86,9 +89,9 @@ void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string m std::vector& queue = std::get>(mQueues); if (queue.empty()) { - const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) { - return !queue.empty(); - }); + const bool eventReceived = + mCondition.wait_for(lock, mEventHappenedTimeout, + [&queue]() REQUIRES(mLock) { return !queue.empty(); }); if (!eventReceived) { FAIL() << "Timed out waiting for event: " << message.c_str(); } @@ -105,9 +108,9 @@ void TestInputListener::assertNotCalled(std::string message) { base::ScopedLockAssertion assumeLocked(mLock); std::vector& queue = std::get>(mQueues); - const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) { - return !queue.empty(); - }); + const bool eventReceived = + mCondition.wait_for(lock, mEventDidNotHappenTimeout, + [&queue]() REQUIRES(mLock) { return !queue.empty(); }); if (eventReceived) { FAIL() << "Unexpected event: " << message.c_str(); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 4262f5adfb..d50c6bcd51 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -32,7 +32,8 @@ protected: virtual ~TestInputListener(); public: - TestInputListener(const std::chrono::milliseconds timeout = 5ms); + TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms, + std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); @@ -75,7 +76,8 @@ private: std::mutex mLock; std::condition_variable mCondition; - const std::chrono::milliseconds mTimeout; + const std::chrono::milliseconds mEventHappenedTimeout; + const std::chrono::milliseconds mEventDidNotHappenTimeout; std::tuple, // std::vector, // -- cgit v1.2.3-59-g8ed1b From 2984b7af4f7969cdc02dea6a1722635cc9a432dd Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 17:06:45 -0700 Subject: InputDispatcher: Fix support for INPUT_FEATURE_NO_INPUT_CHANNEL In preparation for an occlusion detection fix, we are going to begin sending windows with NO_INPUT_CHANNEL to InputFlinger. There are 3 obstacles we address here: 1. InputDispatcher ignores windows with no InputChannel when updating input windows. We modify the code to allow such windows if they have INPUT_FEATURE_NO_INPUT_CHANNEL. 2. The parcelling code currently has an optimization to avoid sending the rest of the fields if token is null. We rebase this optimization on whether or not a name is set. 3. InputWindowHandle::getName checks if there is a token to consider the window invalid. We instead check if a name is set. Bug: 152064592 Test: Existing tests pass Change-Id: I8a85f46b6c44866c7f73daafa5e8ff4c9251c366 --- include/input/InputWindow.h | 2 +- libs/input/InputWindow.cpp | 9 ++------- services/inputflinger/dispatcher/InputDispatcher.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index edaf8f530b..a695a8ffda 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -213,7 +213,7 @@ public: } inline std::string getName() const { - return mInfo.token ? mInfo.name : ""; + return !mInfo.name.empty() ? mInfo.name : ""; } inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 03ca459fb9..6e4c97ded2 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -65,7 +65,7 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { } status_t InputWindowInfo::write(Parcel& output) const { - if (token == nullptr) { + if (name.empty()) { output.writeInt32(0); return OK; } @@ -110,12 +110,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { return ret; } - sp token = from.readStrongBinder(); - if (token == nullptr) { - return ret; - } - - ret.token = token; + ret.token = from.readStrongBinder(); ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ec61b0c63..403e21dffd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3598,8 +3598,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); + continue; } - continue; } if (info->displayId != displayId) { -- cgit v1.2.3-59-g8ed1b From b1462ec353d70a2a80ac6799c390ac6d11d70cba Mon Sep 17 00:00:00 2001 From: arthurhung Date: Mon, 20 Apr 2020 17:18:37 +0800 Subject: Fix KeyEventInterceptTest fail The meta shortcuts may be detected by InputDispatcher and it potentially overwrite the keyCode and metaState. We should pass the overrided values instead of the original one. Test: atest KeyEventInterceptTest Bug: 153606162 Change-Id: Ia7938c30eccf74785d03c72e8b308fd92cf96efb --- services/inputflinger/dispatcher/InputDispatcher.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 403e21dffd..6a45d58f57 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3280,9 +3280,8 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec KeyEntry* injectedEntry = new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), - incomingKey.getDisplayId(), policyFlags, action, flags, - incomingKey.getKeyCode(), incomingKey.getScanCode(), - incomingKey.getMetaState(), incomingKey.getRepeatCount(), + incomingKey.getDisplayId(), policyFlags, action, flags, keyCode, + incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime()); injectedEntries.push(injectedEntry); break; -- cgit v1.2.3-59-g8ed1b From 3d61bc1e6260f227a15d10c72240124207df3861 Mon Sep 17 00:00:00 2001 From: Edgar Arriaga Date: Thu, 16 Apr 2020 18:46:48 -0700 Subject: Optimization: Restrict signing to ACTION_UP and ACTION_DOWN while doing a motion gesture such as scrolling apps Savings. ~2.5% of total system_server time while scrolling on apps drawer screen. Baseline: https://pprof.corp.google.com/?id=732307aa60aa82bf478b0c3f0e6de6be&ignore=sendMessage https://screenshot.googleplex.com/JpWt25tXRJO Optimized: https://pprof.corp.google.com/?id=5040d255411f407662a7f2da0109ca48&ignore=sendMessage https://screenshot.googleplex.com/x81k7vUUKdd Test: atest inputflinger_tests Bug: 154278518 Merged-In: I52dd33f8203fe62bfcbde7be5a1ac45c46064585 Change-Id: I52dd33f8203fe62bfcbde7be5a1ac45c46064585 (cherry picked from commit c6ae4bbc44183811ad84abf5abc56a96843545bc) --- libs/input/InputTransport.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 47 ++++++++++++++-------- services/inputflinger/dispatcher/InputDispatcher.h | 8 +++- 3 files changed, 38 insertions(+), 21 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index ef7cc7d531..11af23e1a2 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -468,7 +468,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; - msg.body.key.hmac = hmac; + msg.body.key.hmac = std::move(hmac); msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -526,7 +526,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; - msg.body.motion.hmac = hmac; + msg.body.motion.hmac = std::move(hmac); msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 6a45d58f57..56d65670ed 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -352,18 +352,16 @@ std::array HmacKeyManager::sign(const VerifiedInputEvent& event) co break; } } - std::vector data; const uint8_t* start = reinterpret_cast(&event); - data.assign(start, start + size); - return sign(data); + return sign(start, size); } -std::array HmacKeyManager::sign(const std::vector& data) const { +std::array HmacKeyManager::sign(const uint8_t* data, size_t size) const { // SHA256 always generates 32-bytes result std::array hash; unsigned int hashLen = 0; - uint8_t* result = HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data.data(), data.size(), - hash.data(), &hashLen); + uint8_t* result = + HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen); if (result == nullptr) { ALOGE("Could not sign the data using HMAC"); return INVALID_HMAC; @@ -2451,11 +2449,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::Type::KEY: { - KeyEntry* keyEntry = static_cast(eventEntry); - VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(*keyEntry); - verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_KEY_EVENT_FLAGS; - verifiedEvent.action = dispatchEntry->resolvedAction; - std::array hmac = mHmacKeyManager.sign(verifiedEvent); + const KeyEntry* keyEntry = static_cast(eventEntry); + std::array hmac = getSignature(*keyEntry, *dispatchEntry); // Publish the key event. status = @@ -2507,12 +2502,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, usingCoords = scaledCoords; } } - VerifiedMotionEvent verifiedEvent = - verifiedMotionEventFromMotionEntry(*motionEntry); - verifiedEvent.actionMasked = - dispatchEntry->resolvedAction & AMOTION_EVENT_ACTION_MASK; - verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; - std::array hmac = mHmacKeyManager.sign(verifiedEvent); + + std::array hmac = getSignature(*motionEntry, *dispatchEntry); // Publish the motion event. status = connection->inputPublisher @@ -2592,6 +2583,28 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } } +const std::array InputDispatcher::getSignature( + const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const { + int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; + if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) { + // Only sign events up and down events as the purely move events + // are tied to their up/down counterparts so signing would be redundant. + VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry); + verifiedEvent.actionMasked = actionMasked; + verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; + return mHmacKeyManager.sign(verifiedEvent); + } + return INVALID_HMAC; +} + +const std::array InputDispatcher::getSignature( + const KeyEntry& keyEntry, const DispatchEntry& dispatchEntry) const { + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry); + verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS; + verifiedEvent.action = dispatchEntry.resolvedAction; + return mHmacKeyManager.sign(verifiedEvent); +} + void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index cbba7e1318..2b9cbcec2e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -62,7 +62,7 @@ public: std::array sign(const VerifiedInputEvent& event) const; private: - std::array sign(const std::vector& data) const; + std::array sign(const uint8_t* data, size_t size) const; const std::array mHmacKey; }; @@ -219,7 +219,11 @@ private: // the pointer stream in order to claim it for a system gesture. std::unordered_map> mGestureMonitorsByDisplay GUARDED_BY(mLock); - HmacKeyManager mHmacKeyManager; + const HmacKeyManager mHmacKeyManager; + const std::array getSignature(const MotionEntry& motionEntry, + const DispatchEntry& dispatchEntry) const; + const std::array getSignature(const KeyEntry& keyEntry, + const DispatchEntry& dispatchEntry) const; // Event injection and synchronization. std::condition_variable mInjectionResultAvailable; -- cgit v1.2.3-59-g8ed1b From bf89a4855d66b88387ce20e4bf6c1637b7213728 Mon Sep 17 00:00:00 2001 From: arthurhung Date: Fri, 17 Apr 2020 17:37:55 +0800 Subject: Allow touch if the new point is not MT_TOOL_PALM We would cancel all touch when we recevied MT_TOOL_PALM if there are accidental touches, that could cause the device can't touch anymore. This ignore these MT_TOOL_PALM points, so if all valid points are released, we could reset the abort flag and the new valid point could be treated as new down event. Test: atest inputflinger_tests Bug: 137221719 Change-Id: Ib376b912d389c97bc5127fd9b7f4ecb168cd266d --- .../reader/mapper/MultiTouchInputMapper.cpp | 1 + services/inputflinger/tests/InputReader_test.cpp | 70 ++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index d77c8c8609..43bd9f1f99 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -255,6 +255,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { getDeviceName().c_str()); cancelTouch(when); } + continue; } if (outCount >= MAX_POINTERS) { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 675b77b688..109edfe582 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -7082,6 +7082,76 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); } +/** + * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger, + * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is + * MT_TOOL_FINGER. + */ +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + NotifyMotionArgs motionArgs; + + // default tool type is finger + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; + processId(mapper, 1); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // Second finger down. + processSlot(mapper, 1); + processPosition(mapper, x2, y2); + processId(mapper, 2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // If the tool type of the first pointer changes to MT_TOOL_PALM, + // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL. + processSlot(mapper, 0); + processId(mapper, 1); + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + + // Ignore the following MOVE and UP events if had detect a palm event. + processSlot(mapper, 1); + processId(mapper, 2); + processPosition(mapper, x3, y3); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // second finger up. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // first finger move, but still in palm + processSlot(mapper, 0); + processId(mapper, 1); + processPosition(mapper, x1 - 1, y1 - 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // second finger down, expect as new finger down. + processSlot(mapper, 1); + processId(mapper, 2); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); +} + // --- MultiTouchInputMapperTest_ExternalDevice --- class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { -- cgit v1.2.3-59-g8ed1b From 9cada03fdc6a99b55450c09c70ff0413603b4c1e Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 17:21:08 -0700 Subject: DO NOT MERGE: InputDispatcher: Consider ownerPid in FLAG_OBSCURED calculations Now that we are receiving an InputWindowHandle for each buffered surface we need to take care not to introduce any behavior changes: 1. Previously a SurfaceView on top of a main app window would not obscure that application for Input purposes (since it was unknown to InputDispatcher). We preserve this semantic by not considering windows from the same ownerPid as occluding eachother. 2. However previously, windows WITH input channels from the same process would generate FLAG_OCCLUDED so we also preserve this semantic by only implementing the new semantic from 1 when channel is null. Bug: 152064592 Test: ObscuredInputTests Change-Id: If45acfd8b63c5ebd68bc219bc0fb6ee1c34650cd --- .../inputflinger/dispatcher/InputDispatcher.cpp | 49 ++++++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4ec61b0c63..0982ea7915 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1988,18 +1988,49 @@ bool InputDispatcher::checkInjectionPermission(const sp& wind return true; } +/** + * Indicate whether one window handle should be considered as obscuring + * another window handle. We only check a few preconditions. Actually + * checking the bounds is left to the caller. + */ +static bool canBeObscuredBy(const sp& windowHandle, + const sp& otherHandle) { + // Compare by token so cloned layers aren't counted + if (haveSameToken(windowHandle, otherHandle)) { + return false; + } + auto info = windowHandle->getInfo(); + auto otherInfo = otherHandle->getInfo(); + if (!otherInfo->visible) { + return false; + } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) { + // In general, if ownerPid is the same we don't want to generate occlusion + // events. This line is now necessary since we are including all Surfaces + // in occlusion calculation, so if we didn't check PID like this SurfaceView + // would occlude their parents. On the other hand before we started including + // all surfaces in occlusion calculation and had this line, we would count + // windows with an input channel from the same PID as occluding, and so we + // preserve this behavior with the getToken() == null check. + return false; + } else if (otherInfo->isTrustedOverlay()) { + return false; + } else if (otherInfo->displayId != info->displayId) { + return false; + } + return true; +} + bool InputDispatcher::isWindowObscuredAtPointLocked(const sp& windowHandle, int32_t x, int32_t y) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector> windowHandles = getWindowHandlesLocked(displayId); for (const sp& otherHandle : windowHandles) { - if (otherHandle == windowHandle) { - break; + if (windowHandle == otherHandle) { + break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId && otherInfo->visible && - !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) { + if (canBeObscuredBy(windowHandle, otherHandle) && + otherInfo->frameContainsPoint(x, y)) { return true; } } @@ -2011,13 +2042,13 @@ bool InputDispatcher::isWindowObscuredLocked(const sp& window const std::vector> windowHandles = getWindowHandlesLocked(displayId); const InputWindowInfo* windowInfo = windowHandle->getInfo(); for (const sp& otherHandle : windowHandles) { - if (otherHandle == windowHandle) { - break; + if (windowHandle == otherHandle) { + break; // All future windows are below us. Exit early. } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId && otherInfo->visible && - !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) { + if (canBeObscuredBy(windowHandle, otherHandle) && + otherInfo->overlaps(windowInfo)) { return true; } } -- cgit v1.2.3-59-g8ed1b From 540dbaee09cd6790951576b8f3de1f85befb4524 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 5 May 2020 18:17:17 -0700 Subject: Generate a new id for each window When input receives a list of windows from surfaceflinger, each window has a unique id. But when we run tests, all ids are the same. This causes some unexpected test failures for the cases where we have more than 1 window per display. To avoid this issue, mirror the surfaceflinger approach of generating the ids by keeping a static variable that gets incremented every time we create a new window Bug: 143459140 Test: atest inputflinger_tests Change-Id: I36731e78e16892b4bf48d6eba8db3e4c2684b54d --- services/inputflinger/tests/InputDispatcher_test.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index e0d32a0382..365d43dd16 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -634,7 +634,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; - mInfo.id = 0; + mInfo.id = sId++; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; @@ -672,8 +672,6 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } - void setId(int32_t id) { mInfo.id = id; } - void setWindowScale(float xScale, float yScale) { mInfo.windowXScale = xScale; mInfo.windowYScale = yScale; @@ -755,8 +753,11 @@ public: private: const std::string mName; std::unique_ptr mInputReceiver; + static std::atomic sId; // each window gets a unique id, like in surfaceflinger }; +std::atomic FakeWindowHandle::sId{1}; + static int32_t injectKeyDown(const sp& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { KeyEvent event; @@ -1910,14 +1911,12 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); - mWindow1->setId(0); mWindow1->setFrame(Rect(0, 0, 100, 100)); mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", ADISPLAY_ID_DEFAULT, mWindow1->getToken()); mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); - mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); -- cgit v1.2.3-59-g8ed1b From d352cb3827b5298e9c9127f7dd75cdf2cd5376de Mon Sep 17 00:00:00 2001 From: arthurhung Date: Tue, 28 Apr 2020 17:09:28 +0800 Subject: Fix consumer closed input channel cause an error occurred (2/2) An input channel specifies the file descriptors used to send input events to a window in another process. And All of the file descriptors open in the calling process shall be closed when process terminated. The server side could receive the socket broken event when the process terminated, we should do the unregister channel before close the file descriptor or do it automatically without error if there is no valid window. Bug: 133782251 Test: open app and exit by back key or recent apps, check if any error log occurs. Change-Id: I59d0084c2c771544e7ee226ce53c574f60c6b988 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index baf2f2b917..d636d3e2b8 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2748,7 +2748,10 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn // about them. - notify = !connection->monitor; + const bool stillHaveWindowHandle = + d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != + nullptr; + notify = !connection->monitor && stillHaveWindowHandle; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", -- cgit v1.2.3-59-g8ed1b From fb9fcdae2f67ebef2ab6522fd2b49a57f9c28dd2 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 4 May 2020 14:59:19 -0700 Subject: Add test to call setInputWindows twice Currently, I'm observing some strange behaviour, where calling setInputWindows twice results in the touchable region becoming empty. Add this test to R to see what's going on, and potentially bisect on this. Bug: 143459140 Test: atest inputflinger_tests Change-Id: Ia0acef5d4ee4acc29d20174fe44c9f94172ccd96 --- libs/ui/Region.cpp | 5 ++ libs/ui/tests/Region_test.cpp | 15 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 73 ++++++++++++++++++---- 3 files changed, 82 insertions(+), 11 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 82ce757d5a..e01309b679 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -224,6 +224,11 @@ Region& Region::operator = (const Region& rhs) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif + if (this == &rhs) { + // Already equal to itself + return *this; + } + mStorage.clear(); mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end()); return *this; diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp index b104a46364..c6b826d66e 100644 --- a/libs/ui/tests/Region_test.cpp +++ b/libs/ui/tests/Region_test.cpp @@ -152,5 +152,20 @@ TEST_F(RegionTest, Random_TJunction) { } } +TEST_F(RegionTest, EqualsToSelf) { + Region touchableRegion; + touchableRegion.orSelf(Rect(0, 0, 100, 100)); + + ASSERT_TRUE(touchableRegion.contains(50, 50)); + + // Compiler prevents us from directly calling 'touchableRegion = touchableRegion' + Region& referenceTouchableRegion = touchableRegion; + touchableRegion = referenceTouchableRegion; + + ASSERT_FALSE(touchableRegion.isEmpty()); + + ASSERT_TRUE(touchableRegion.contains(50, 50)); +} + }; // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 365d43dd16..e94737ff96 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -811,13 +811,15 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t } static int32_t injectMotionDown(const sp& dispatcher, int32_t source, - int32_t displayId, int32_t x = 100, int32_t y = 200) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y); + int32_t displayId, const PointF& location = {100, 200}) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x, + location.y); } static int32_t injectMotionUp(const sp& dispatcher, int32_t source, - int32_t displayId, int32_t x = 100, int32_t y = 200) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y); + int32_t displayId, const PointF& location = {100, 200}) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x, + location.y); } static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { @@ -881,6 +883,55 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { window->consumeMotionDown(ADISPLAY_ID_DEFAULT); } +/** + * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues. + * To ensure that window receives only events that were directly inside of it, add + * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input + * when finding touched windows. + * This test serves as a sanity check for the next test, where setInputWindows is + * called twice. + */ +TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + + // Window should receive motion event. + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); +} + +/** + * Calling setInputWindows twice, with the same info, should not cause any issues. + * To ensure that window receives only events that were directly inside of it, add + * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input + * when finding touched windows. + */ +TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { + sp application = new FakeApplicationHandle(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + + // Window should receive motion event. + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); +} + // The foreground window should receive the first touch down event. TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { sp application = new FakeApplicationHandle(); @@ -1822,7 +1873,6 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); - mFocusedWindowTouchPoint = 60; // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -1843,15 +1893,16 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { protected: sp mUnfocusedWindow; sp mFocusedWindow; - int32_t mFocusedWindowTouchPoint; + static constexpr PointF FOCUSED_WINDOW_TOUCH_POINT = {60, 60}; }; // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action // DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received // the onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20)) + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {20, 20})) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; mUnfocusedWindow->consumeMotionDown(); @@ -1863,8 +1914,8 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Succe // DOWN on the window that doesn't have focus. Ensure no window received the // onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20)) + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20})) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; mFocusedWindow->consumeMotionDown(); @@ -1890,7 +1941,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - mFocusedWindowTouchPoint, mFocusedWindowTouchPoint)) + FOCUSED_WINDOW_TOUCH_POINT)) << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; mFocusedWindow->consumeMotionDown(); -- cgit v1.2.3-59-g8ed1b From 097c3db9fc66ffb6f1b55401229bcc43d885a3ed Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 6 May 2020 14:18:38 -0700 Subject: Use for input-related timeouts There is a lot of confusion in input tests about the units used for the timeouts. In a lot of cases, the timeouts are set too short, which causes failures when they start getting enforced. To avoid this, use std::chrono::duration for the timeouts. Ideally, we should convert InputWindowInfo and InputApplicationInfo to use std::chrono::nanoseconds, but that would be a larger change reserved for a future release. Bug: 143459140 Test: atest inputflinger_tests libinput_tests inputflinger_benchmarks Change-Id: Ie7536e8a042a71b372f03314501e0d635a6ac1d4 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 4 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 8 +-- .../inputflinger/dispatcher/InputDispatcher.cpp | 11 ++- services/inputflinger/dispatcher/InputDispatcher.h | 3 +- .../dispatcher/include/InputDispatcherInterface.h | 4 +- .../inputflinger/tests/InputDispatcher_test.cpp | 82 +++++++++++----------- 6 files changed, 56 insertions(+), 56 deletions(-) (limited to 'services/inputflinger') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index c59afba87c..5188a09c4b 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -178,7 +178,7 @@ private: mInputInfo.name = "Test info"; mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; - mInputInfo.dispatchingTimeout = 100000; + mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5); mInputInfo.globalScaleFactor = 1.0; mInputInfo.canReceiveKeys = true; mInputInfo.hasFocus = true; @@ -196,7 +196,7 @@ private: InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; - aInfo.dispatchingTimeout = 100000; + aInfo.dispatchingTimeout = seconds_to_nanoseconds(5); mInputInfo.applicationInfo = aInfo; } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 7c5c9c5f0c..2116cbcc7f 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -28,8 +28,8 @@ static const int32_t DEVICE_ID = 1; static const int32_t INJECTOR_PID = 999; static const int32_t INJECTOR_UID = 1001; -static const int32_t INJECT_EVENT_TIMEOUT = 5000; -static const int32_t DISPATCHING_TIMEOUT = 100000; +static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s; +static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; static nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); @@ -98,7 +98,7 @@ public: virtual ~FakeApplicationHandle() {} virtual bool updateInfo() { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } }; @@ -163,7 +163,7 @@ public: mInfo.name = "FakeWindowHandle"; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); mInfo.frameLeft = mFrame.left; mInfo.frameTop = mFrame.top; mInfo.frameRight = mFrame.right; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index baf2f2b917..2011551c38 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3273,14 +3273,14 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, - int32_t timeoutMillis, uint32_t policyFlags) { + std::chrono::milliseconds timeout, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", - event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); + "syncMode=%d, timeout=%lld, policyFlags=0x%08x", + event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); #endif - nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); + nsecs_t endTime = now() + std::chrono::duration_cast(timeout).count(); policyFlags |= POLICY_FLAG_INJECTED; if (hasInjectionPermission(injectorPid, injectorUid)) { @@ -3467,8 +3467,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } // release lock #if DEBUG_INJECTION - ALOGD("injectInputEvent - Finished with result %d. " - "injectorPid=%d, injectorUid=%d", + ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d", injectionResult, injectorPid, injectorUid); #endif diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 2b9cbcec2e..f6b5da3904 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -103,7 +103,8 @@ public: virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + int32_t injectorUid, int32_t syncMode, + std::chrono::milliseconds timeout, uint32_t policyFlags) override; virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 09dc92c8fa..9b002f437c 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -90,8 +90,8 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) = 0; + int32_t injectorUid, int32_t syncMode, + std::chrono::milliseconds timeout, uint32_t policyFlags) = 0; /* * Check whether InputEvent actually happened by checking the signature of the event. diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index e94737ff96..5db7b00bc3 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -313,18 +313,18 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { INVALID_HMAC, /*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, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, 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, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject key events with ACTION_MULTIPLE."; } @@ -350,9 +350,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. @@ -363,9 +363,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer down index too large."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, @@ -375,9 +375,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. @@ -388,9 +388,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer up index too large."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, @@ -400,9 +400,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. @@ -412,9 +412,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with 0 pointers."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, @@ -423,9 +423,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with more than MAX_POINTERS pointers."; // Rejects motion events with invalid pointer ids. @@ -436,9 +436,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; @@ -448,9 +448,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with pointer ids greater than MAX_POINTER_ID."; // Rejects motion events with duplicate pointer ids. @@ -462,9 +462,9 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, + INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) << "Should reject motion events with duplicate pointer ids."; } @@ -490,16 +490,16 @@ TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { } // --- InputDispatcherTest SetInputWindowTest --- -static constexpr int32_t INJECT_EVENT_TIMEOUT = 500; -static constexpr nsecs_t DISPATCHING_TIMEOUT = seconds_to_nanoseconds(5); +static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms; +static constexpr std::chrono::duration DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: FakeApplicationHandle() {} virtual ~FakeApplicationHandle() {} - virtual bool updateInfo() { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + virtual bool updateInfo() override { + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } }; @@ -638,7 +638,7 @@ public: mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); mInfo.frameLeft = 0; mInfo.frameTop = 0; mInfo.frameRight = WIDTH; -- cgit v1.2.3-59-g8ed1b From b1a1627da8acb98229f5bfa1ff2a44b06114b962 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 6 May 2020 16:09:19 -0700 Subject: Rename ANR -> Anr To follow the code style, rename ANR -> Anr, because we are about to add a lot more ANR-related stuff, and we don't want to keep adding new code that doesn't follow the code style. Bug: 143459140 Test: build only Change-Id: I5a97ba4dcd8baa8a34fbb790e68fb63fd3c3ca75 --- .../benchmarks/InputDispatcher_benchmarks.cpp | 2 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 44 +++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 8 ++-- .../include/InputDispatcherPolicyInterface.h | 2 +- .../inputflinger/tests/InputDispatcher_test.cpp | 34 +++++++---------- 5 files changed, 42 insertions(+), 48 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 7c5c9c5f0c..395da873f2 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -47,7 +47,7 @@ protected: private: virtual void notifyConfigurationChanged(nsecs_t) override {} - virtual nsecs_t notifyANR(const sp&, const sp&, + virtual nsecs_t notifyAnr(const sp&, const sp&, const std::string& name) override { ALOGE("The window is not responding : %s", name.c_str()); return 0; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index baf2f2b917..d49f149772 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -531,7 +531,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } // Get ready to dispatch the event. - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); } // Now we have an event to dispatch. @@ -888,7 +888,7 @@ void InputDispatcher::drainInboundQueueLocked() { void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); mPendingEvent = nullptr; } @@ -1299,7 +1299,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, + onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, mInputTargetWaitStartTime, reason); // Force poll loop to wake up immediately on next iteration once we get the @@ -1352,7 +1352,7 @@ nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t current return 0; } -void InputDispatcher::resetANRTimeoutsLocked() { +void InputDispatcher::resetAnrTimeoutsLocked() { if (DEBUG_FOCUS) { ALOGD("Resetting ANR timeouts."); } @@ -3800,12 +3800,12 @@ void InputDispatcher::setFocusedApplication( if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { if (oldFocusedApplicationHandle != inputApplicationHandle) { if (oldFocusedApplicationHandle != nullptr) { - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); } mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } } else if (oldFocusedApplicationHandle != nullptr) { - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } @@ -3886,7 +3886,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); } if (mDispatchEnabled && !enabled) { @@ -4026,7 +4026,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetANRTimeoutsLocked(); + resetAnrTimeoutsLocked(); mTouchStatesByDisplay.clear(); mLastHoverWindowHandle.clear(); @@ -4526,7 +4526,7 @@ void InputDispatcher::onFocusChangedLocked(const sp& oldFocus postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onANRLocked(nsecs_t currentTime, +void InputDispatcher::onAnrLocked(nsecs_t currentTime, const sp& applicationHandle, const sp& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) { @@ -4543,19 +4543,19 @@ void InputDispatcher::onANRLocked(nsecs_t currentTime, localtime_r(&t, &tm); char timestr[64]; strftime(timestr, sizeof(timestr), "%F %T", &tm); - mLastANRState.clear(); - mLastANRState += INDENT "ANR:\n"; - mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastANRState += + mLastAnrState.clear(); + mLastAnrState += INDENT "ANR:\n"; + mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr); + mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); - mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); - mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); - mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); - dumpDispatchStateLocked(mLastANRState); + mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); + mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); + mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason); + dumpDispatchStateLocked(mLastAnrState); std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doNotifyANRLockedInterruptible); + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; @@ -4591,13 +4591,13 @@ void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* comm mLock.lock(); } -void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { +void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) { sp token = commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); nsecs_t newTimeout = - mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason); + mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); @@ -4958,9 +4958,9 @@ void InputDispatcher::dump(std::string& dump) { dump += "Input Dispatcher State:\n"; dumpDispatchStateLocked(dump); - if (!mLastANRState.empty()) { + if (!mLastAnrState.empty()) { dump += "\nInput Dispatcher State at time of last ANR:\n"; - dump += mLastANRState; + dump += mLastAnrState; } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 2b9cbcec2e..a2c727fdbc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -314,7 +314,7 @@ private: int32_t mFocusedDisplayId GUARDED_BY(mLock); // Dispatcher state at time of last ANR. - std::string mLastANRState GUARDED_BY(mLock); + std::string mLastAnrState GUARDED_BY(mLock); // Dispatch inbound events. bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) @@ -360,7 +360,7 @@ private: const sp& inputConnectionToken) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetANRTimeoutsLocked() REQUIRES(mLock); + void resetAnrTimeoutsLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry& entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, @@ -468,7 +468,7 @@ private: REQUIRES(mLock); void onFocusChangedLocked(const sp& oldFocus, const sp& newFocus) REQUIRES(mLock); - void onANRLocked(nsecs_t currentTime, const sp& applicationHandle, + void onAnrLocked(nsecs_t currentTime, const sp& applicationHandle, const sp& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); @@ -477,7 +477,7 @@ private: REQUIRES(mLock); void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 4214488f04..667af9bbd8 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -47,7 +47,7 @@ public: /* Notifies the system that an application is not responding. * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyANR(const sp& inputApplicationHandle, + virtual nsecs_t notifyAnr(const sp& inputApplicationHandle, const sp& token, const std::string& reason) = 0; /* Notifies the system that an input channel is unrecoverably broken. */ diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 365d43dd16..2b74868645 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -127,19 +127,16 @@ private: mConfigurationChangedTime = when; } - virtual nsecs_t notifyANR(const sp&, - const sp&, - const std::string&) { + virtual nsecs_t notifyAnr(const sp&, const sp&, + const std::string&) override { return 0; } - virtual void notifyInputChannelBroken(const sp&) { - } + virtual void notifyInputChannelBroken(const sp&) override {} - virtual void notifyFocusChanged(const sp&, const sp&) { - } + virtual void notifyFocusChanged(const sp&, const sp&) override {} - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) { + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { *outConfig = mConfig; } @@ -160,19 +157,17 @@ private: return true; } - virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) { - } + virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {} - virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) { - } + virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} - virtual nsecs_t interceptKeyBeforeDispatching(const sp&, - const KeyEvent*, uint32_t) { + virtual nsecs_t interceptKeyBeforeDispatching(const sp&, const KeyEvent*, + uint32_t) override { return 0; } - virtual bool dispatchUnhandledKey(const sp&, - const KeyEvent*, uint32_t, KeyEvent*) { + virtual bool dispatchUnhandledKey(const sp&, const KeyEvent*, uint32_t, + KeyEvent*) override { return false; } @@ -184,14 +179,13 @@ private: mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask); } - virtual void pokeUserActivity(nsecs_t, int32_t) { - } + virtual void pokeUserActivity(nsecs_t, int32_t) override {} - virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) { + virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; } - virtual void onPointerDownOutsideFocus(const sp& newToken) { + virtual void onPointerDownOutsideFocus(const sp& newToken) override { mOnPointerDownToken = newToken; } -- cgit v1.2.3-59-g8ed1b From 7f0a439ef705d36182a36e07351decf124e7c5f3 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Mar 2020 20:49:09 -0700 Subject: Reduce the usage of goto statements in dispatcher Currently, there are a lot of uses of goto statements in InputDispatcher, which makes the code hard to follow. Instead, remove some unused code and reduce the goto usage. Bug: 143459140 Test: presubmit Change-Id: I0ef0b3a0820ce643486cec5d9cbeb0f593344ee6 Merged-In: I0ef0b3a0820ce643486cec5d9cbeb0f593344ee6 (cherry picked from commit 767917f075b1abfd8cc5905843021b7c3a468be9) --- .../inputflinger/dispatcher/InputDispatcher.cpp | 212 +++++++++------------ services/inputflinger/dispatcher/InputDispatcher.h | 4 +- 2 files changed, 90 insertions(+), 126 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 155021d325..bcfc7acdf9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1152,14 +1152,16 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent } setInjectionResult(entry, injectionResult); + if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) { + ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent)); + return true; + } if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) { - CancelationOptions::Mode mode(isPointerEvent - ? CancelationOptions::CANCEL_POINTER_EVENTS - : CancelationOptions::CANCEL_NON_POINTER_EVENTS); - CancelationOptions options(mode, "input event injection failed"); - synthesizeCancelationEventsForMonitorsLocked(options); - } + CancelationOptions::Mode mode(isPointerEvent + ? CancelationOptions::CANCEL_POINTER_EVENTS + : CancelationOptions::CANCEL_NON_POINTER_EVENTS); + CancelationOptions options(mode, "input event injection failed"); + synthesizeCancelationEventsForMonitorsLocked(options); return true; } @@ -1345,13 +1347,6 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( } } -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) { - if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - return currentTime - mInputTargetWaitStartTime; - } - return 0; -} - void InputDispatcher::resetAnrTimeoutsLocked() { if (DEBUG_FOCUS) { ALOGD("Resetting ANR timeouts."); @@ -1394,7 +1389,6 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime) { - int32_t injectionResult; std::string reason; int32_t displayId = getTargetDisplayId(entry); @@ -1407,54 +1401,38 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // then drop the event. if (focusedWindowHandle == nullptr) { if (focusedApplicationHandle != nullptr) { - injectionResult = - handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - nullptr, nextWakeupTime, - "Waiting because no window has focus but there is " - "a focused application that may eventually add a " - "window when it finishes starting up."); - goto Unresponsive; + return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + nullptr, nextWakeupTime, + "Waiting because no window has focus but there is " + "a focused application that may eventually add a " + "window when it finishes starting up."); } ALOGI("Dropping event because there is no focused window or focused application in display " "%" PRId32 ".", displayId); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; + return INPUT_EVENT_INJECTION_FAILED; } // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - goto Failed; + return INPUT_EVENT_INJECTION_PERMISSION_DENIED; } // Check whether the window is ready for more input. reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); if (!reason.empty()) { - injectionResult = - handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - focusedWindowHandle, nextWakeupTime, reason.c_str()); - goto Unresponsive; + return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + focusedWindowHandle, nextWakeupTime, reason.c_str()); } // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; addWindowTargetLocked(focusedWindowHandle, InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), inputTargets); // Done. -Failed: -Unresponsive: - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); - if (DEBUG_FOCUS) { - ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); - } - return injectionResult; + return INPUT_EVENT_INJECTION_SUCCEEDED; } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, @@ -1751,10 +1729,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry, "touched"); if (!reason.empty()) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr, - touchedWindow.windowHandle, - nextWakeupTime, reason.c_str()); - goto Unresponsive; + return handleTargetsNotReadyLocked(currentTime, entry, nullptr, + touchedWindow.windowHandle, nextWakeupTime, + reason.c_str()); } } } @@ -1814,98 +1791,85 @@ Failed: } } + if (injectionPermission != INJECTION_PERMISSION_GRANTED) { + return injectionResult; + } + // Update final pieces of touch state if the injector had permission. - if (injectionPermission == INJECTION_PERMISSION_GRANTED) { - if (!wrongDevice) { - if (switchedDevice) { + if (!wrongDevice) { + if (switchedDevice) { + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Switched to a different device."); + } + *outConflictingPointerActions = true; + } + + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (oldState && oldState->down) { if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Switched to a different device."); + ALOGD("Conflicting pointer actions: Hover received while pointer was " + "down."); } *outConflictingPointerActions = true; } - - if (isHoverAction) { - // Started hovering, therefore no longer down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Hover received while pointer was " - "down."); - } - *outConflictingPointerActions = true; - } - mTempTouchState.reset(); - if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || - maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mTempTouchState.deviceId = entry.deviceId; - mTempTouchState.source = entry.source; - mTempTouchState.displayId = displayId; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP || - maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - mTempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Down received while already down."); - } - *outConflictingPointerActions = true; + mTempTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mTempTouchState.deviceId = entry.deviceId; + mTempTouchState.source = entry.source; + mTempTouchState.displayId = displayId; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + // All pointers up or canceled. + mTempTouchState.reset(); + } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + // First pointer went down. + if (oldState && oldState->down) { + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Down received while already down."); } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - if (isSplit) { - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - - for (size_t i = 0; i < mTempTouchState.windows.size();) { - TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i); - continue; - } + *outConflictingPointerActions = true; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + // One pointer went up. + if (isSplit) { + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; + + for (size_t i = 0; i < mTempTouchState.windows.size();) { + TouchedWindow& touchedWindow = mTempTouchState.windows[i]; + if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i); + continue; } - i += 1; } + i += 1; } } + } - // Save changes unless the action was scroll in which case the temporary touch - // state was only valid for this one action. - if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (mTempTouchState.displayId >= 0) { - if (oldStateIndex >= 0) { - mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState); - } else { - mTouchStatesByDisplay.add(displayId, mTempTouchState); - } - } else if (oldStateIndex >= 0) { - mTouchStatesByDisplay.removeItemsAt(oldStateIndex); + // Save changes unless the action was scroll in which case the temporary touch + // state was only valid for this one action. + if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { + if (mTempTouchState.displayId >= 0) { + if (oldStateIndex >= 0) { + mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState); + } else { + mTouchStatesByDisplay.add(displayId, mTempTouchState); } + } else if (oldStateIndex >= 0) { + mTouchStatesByDisplay.removeItemsAt(oldStateIndex); } - - // Update hover state. - mLastHoverWindowHandle = newHoverWindowHandle; - } - } else { - if (DEBUG_FOCUS) { - ALOGD("Not updating touch focus because injection was denied."); } - } -Unresponsive: - // Reset temporary touch state to ensure we release unnecessary references to input channels. - mTempTouchState.reset(); - - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); - if (DEBUG_FOCUS) { - ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); + // Update hover state. + mLastHoverWindowHandle = newHoverWindowHandle; } + return injectionResult; } @@ -4661,6 +4625,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c dispatchEntry->eventEntry->appendDescription(msg); ALOGI("%s", msg.c_str()); } + reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); bool restartEvent; if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { @@ -4895,9 +4860,8 @@ KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { return event; } -void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry, - int32_t injectionResult, - nsecs_t timeSpentWaitingForApplication) { +void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration, + const Connection& connection, bool handled) { // TODO Write some statistics about how long we spend waiting. } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 670d0e1c96..d122282fbe 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -497,8 +497,8 @@ private: LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; void reportTouchEventForStatistics(const MotionEntry& entry); - void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); + void reportDispatchStatistics(std::chrono::nanoseconds eventDuration, + const Connection& connection, bool handled); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const sp& connection); void traceWaitQueueLength(const sp& connection); -- cgit v1.2.3-59-g8ed1b From cd899e8fc9e3017eb1dedd6124abf89a6054f509 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 8 May 2020 09:24:29 -0700 Subject: Add locks to InputDispatcher_test The variables inside the policy are being called from the test thread and the InputDispatcher thread. They are being accessed without a lock today. This is OK because we are waiting for dispatcher to go to idle today, but in ag/10847700 we will have dispatcher not idle, and need to have a way to wait for a policy callback from the test thread with a certain timeout. Add locks to the policy variables here to reduce the diff. Bug: 143459140 Test: atest inputflinger_tests Change-Id: I77f5ec15cdfd55ce4c4dfcb501071b2c7c961b1b --- services/inputflinger/dispatcher/Entry.cpp | 3 +++ services/inputflinger/include/InputListener.h | 1 - .../inputflinger/tests/InputDispatcher_test.cpp | 25 ++++++++++++++++------ 3 files changed, 22 insertions(+), 7 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 49630ad436..a1eb0079df 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -37,6 +37,8 @@ static std::string motionActionToString(int32_t action) { return "MOVE"; case AMOTION_EVENT_ACTION_UP: return "UP"; + case AMOTION_EVENT_ACTION_CANCEL: + return "CANCEL"; case AMOTION_EVENT_ACTION_POINTER_DOWN: return "POINTER_DOWN"; case AMOTION_EVENT_ACTION_POINTER_UP: @@ -57,6 +59,7 @@ static std::string keyActionToString(int32_t action) { } return StringPrintf("%" PRId32, action); } + VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index f8d0150e87..8317b051e3 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -22,7 +22,6 @@ #include #include #include -#include namespace android { diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index eeff757b48..f33cc65c2a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -83,9 +83,13 @@ public: args.displayId); } - void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); } + void assertFilterInputEventWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_EQ(nullptr, mFilteredEvent); + } void assertNotifyConfigurationChangedWasCalled(nsecs_t when) { + std::scoped_lock lock(mLock); ASSERT_TRUE(mConfigurationChangedTime) << "Timed out waiting for configuration changed call"; ASSERT_EQ(*mConfigurationChangedTime, when); @@ -93,6 +97,7 @@ public: } void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { + std::scoped_lock lock(mLock); ASSERT_TRUE(mLastNotifySwitch); // We do not check id because it is not exposed to the policy EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime); @@ -103,11 +108,13 @@ public: } void assertOnPointerDownEquals(const sp& touchedToken) { + std::scoped_lock lock(mLock); ASSERT_EQ(touchedToken, mOnPointerDownToken); mOnPointerDownToken.clear(); } void assertOnPointerDownWasNotCalled() { + std::scoped_lock lock(mLock); ASSERT_TRUE(mOnPointerDownToken == nullptr) << "Expected onPointerDownOutsideFocus to not have been called"; } @@ -118,12 +125,14 @@ public: } private: - std::unique_ptr mFilteredEvent; - std::optional mConfigurationChangedTime; - sp mOnPointerDownToken; - std::optional mLastNotifySwitch; + std::mutex mLock; + std::unique_ptr mFilteredEvent GUARDED_BY(mLock); + std::optional mConfigurationChangedTime GUARDED_BY(mLock); + sp mOnPointerDownToken GUARDED_BY(mLock); + std::optional mLastNotifySwitch GUARDED_BY(mLock); virtual void notifyConfigurationChanged(nsecs_t when) override { + std::scoped_lock lock(mLock); mConfigurationChangedTime = when; } @@ -141,6 +150,7 @@ private: } virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override { + std::scoped_lock lock(mLock); switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast(inputEvent); @@ -173,6 +183,7 @@ private: virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) override { + std::scoped_lock lock(mLock); /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is * essentially a passthrough for notifySwitch. */ @@ -186,11 +197,13 @@ private: } virtual void onPointerDownOutsideFocus(const sp& newToken) override { + std::scoped_lock lock(mLock); mOnPointerDownToken = newToken; } void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action, int32_t displayId) { + std::scoped_lock lock(mLock); ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; ASSERT_EQ(mFilteredEvent->getType(), type); @@ -485,7 +498,7 @@ TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { // --- InputDispatcherTest SetInputWindowTest --- static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms; -static constexpr std::chrono::duration DISPATCHING_TIMEOUT = 5s; +static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: -- cgit v1.2.3-59-g8ed1b From edd1360ad34c0777127b0df700f197ad4619ef66 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Mon, 13 Apr 2020 17:24:34 -0700 Subject: SurfaceFlinger: Send all BufferedLayers to InputFlinger Currently if an InputWindow is obscured by a window with FLAG_NO_INPUT_CHANNEL or a Surface with no window (and input) at all then InputDispatcher will not be aware of the window and it will fail to generate FLAG_(PARTIALLY)_OCLUDED for windows underneath. To fix this we make sure we generate an InputWindow for every buffered element on the screen and make them all known to InputDispatcher. Bug: 152064592 Test: ObscuredInputTests Change-Id: I90a813be9b650dceb0a20ffbf33aa27f95d38771 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 3 ++- services/surfaceflinger/BufferLayer.h | 5 +++++ services/surfaceflinger/Layer.cpp | 12 +++++++++++- services/surfaceflinger/Layer.h | 12 +++++++++++- services/surfaceflinger/SurfaceFlinger.cpp | 2 +- 5 files changed, 30 insertions(+), 4 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 1dbcf98652..e8cadeeba0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3653,7 +3653,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } - if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { + if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) && + (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) { const sp& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 97ffe6f528..0e69f60922 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -148,6 +148,11 @@ private: virtual status_t updateActiveBuffer() = 0; virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; + // We generate InputWindowHandles for all buffered layers regardless of whether they + // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion + // detection. + bool needsInputInfo() const override { return true; } + protected: struct BufferInfo { nsecs_t mDesiredPresentTime; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 17458e3c9e..81a63dfd1c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2323,6 +2323,16 @@ bool Layer::isRemovedFromCurrentState() const { } InputWindowInfo Layer::fillInputInfo() { + if (!hasInputInfo()) { + mDrawingState.inputInfo.name = getName(); + mDrawingState.inputInfo.ownerUid = mCallingUid; + mDrawingState.inputInfo.ownerPid = mCallingPid; + mDrawingState.inputInfo.inputFeatures = + InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; + mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; + mDrawingState.inputInfo.displayId = getLayerStack(); + } + InputWindowInfo info = mDrawingState.inputInfo; info.id = sequence; @@ -2404,7 +2414,7 @@ sp Layer::getClonedRoot() { return mDrawingParent.promote()->getClonedRoot(); } -bool Layer::hasInput() const { +bool Layer::hasInputInfo() const { return mDrawingState.inputInfo.token != nullptr; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 3fa935f314..1bec438743 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -947,7 +947,17 @@ public: void setInputInfo(const InputWindowInfo& info); InputWindowInfo fillInputInfo(); - bool hasInput() const; + /** + * Returns whether this layer has an explicitly set input-info. + */ + bool hasInputInfo() const; + /** + * Return whether this layer needs an input info. For most layer types + * this is only true if they explicitly set an input-info but BufferLayer + * overrides this so we can generate input-info for Buffered layers that don't + * have them (for input occlusion detection checks). + */ + virtual bool needsInputInfo() const { return hasInputInfo(); } protected: compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4cd1c9bd9b..1b2043f826 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2885,7 +2885,7 @@ void SurfaceFlinger::updateInputWindowInfo() { std::vector inputHandles; mDrawingState.traverseInReverseZOrder([&](Layer* layer) { - if (layer->hasInput()) { + if (layer->needsInputInfo()) { // When calculating the screen bounds we ignore the transparent region since it may // result in an unwanted offset. inputHandles.push_back(layer->fillInputInfo()); -- cgit v1.2.3-59-g8ed1b From a0d2b809f012ca8447659d08dd8172b7e0d35964 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 13 May 2020 14:00:31 -0700 Subject: Pass BTN_GEAR_DOWN and BTN_GEAR_UP to apps The Steam controller has 2 large 'paddle' buttons on the back. When used in USB mode with hid-steam, this sends BTN_GEAR_DOWN and BTN_GEAR_UP to the user space. But today, these events never reach the apps, even if they have proper mappings. This is because these buttons are not considered to be "keyboard or gamepad". Adjust the checks to include these 2 buttons in KeyboardInputMapper. Test: connect Steam controller over USB and check that the paddle buttons produce events visible in the "gamepad tester" app Test: atest inputflinger_tests Bug: 147434575 Change-Id: I43f221c546ccc1277d578a185465bd5eadac9e8e --- services/inputflinger/reader/EventHub.cpp | 2 +- .../reader/mapper/KeyboardInputMapper.cpp | 2 +- services/inputflinger/tests/InputReader_test.cpp | 22 ++++++++++++++++++++++ services/inputflinger/tests/UinputDevice.cpp | 3 +++ services/inputflinger/tests/UinputDevice.h | 10 ++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 264d2879e4..a1514af668 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -1318,7 +1318,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || - containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), + containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL), sizeof_bit_array(KEY_MAX + 1)); bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) || diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 7be4a58cc3..e0092210a0 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -233,7 +233,7 @@ void KeyboardInputMapper::process(const RawEvent* rawEvent) { } bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE || scanCode >= KEY_OK || + return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 109edfe582..c457a1525f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1848,6 +1848,28 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { ASSERT_LE(prevTimestamp, keyArgs.eventTime); } +/** + * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons + * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP + * are passed to the listener. + */ +static_assert(BTN_GEAR_DOWN == BTN_WHEEL); +TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) { + std::unique_ptr controller = createUinputDevice(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + NotifyKeyArgs keyArgs; + + controller->pressAndReleaseKey(BTN_GEAR_DOWN); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP + ASSERT_EQ(BTN_GEAR_DOWN, keyArgs.scanCode); + + controller->pressAndReleaseKey(BTN_GEAR_UP); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP + ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode); +} + // --- TouchProcessTest --- class TouchIntegrationTest : public InputReaderIntegrationTest { protected: diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 10e7293770..0659511d25 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -125,6 +125,9 @@ void UinputHomeKey::pressAndReleaseHomeKey() { pressAndReleaseKey(KEY_HOME); } +// --- UinputSteamController +UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {} + // --- UinputTouchScreen --- UinputTouchScreen::UinputTouchScreen(const Rect* size) : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index ec3cd9fdba..22d1f630fc 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -108,6 +108,16 @@ private: UinputHomeKey(); }; +// A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key. +class UinputSteamController : public UinputKeyboard { +public: + template + friend std::unique_ptr createUinputDevice(Ts... args); + +private: + UinputSteamController(); +}; + // --- UinputTouchScreen --- // A touch screen device with specific size. class UinputTouchScreen : public UinputDevice { -- cgit v1.2.3-59-g8ed1b From de1bc4a4e384ca789ca5040d4360d0e70df87da0 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 May 2020 22:39:43 -0700 Subject: Process focus events first While refactoring ANR logic, we discovered that an existing Q behaviour was broken by the focus refactoring. The behaviour aims to prevent keys from being sent to incorrect windows. The desired behaviour is as follows: if there are any unfinished events, then do not process key events (keep them pending), which would allow these key events to go to a new window, if one becomes focused. The intent is that if the user hits "Alt+Tab" to switch to another window, and starts typing right away, we want to make sure that we have processed the focus event for the new window prior to determining where to send the new key events after Alt+Tab. The behaviour is broken today because we put the focus events at the back of the queue. So even if we process the focus event first, it would still go to the app after the key event, which would mean that the new window may receive it, but would drop it because that window does not yet have focus (since we haven't sent the focused event to it). The proposed solution is to always put the focus events in the front of the queue. But, we still need to preserve the relative order of focus events. To account for that, we put a new focus event always at the front of the queue, but behind any other focus events that may be there. Bug: 143459140 Test: added in ANR CL because this behaviour is updated there. Change-Id: I95256bd3f57988d1990cf1f21267c8f405892e8e Merged-In: I95256bd3f57988d1990cf1f21267c8f405892e8e --- .../inputflinger/dispatcher/InputDispatcher.cpp | 85 ++++++++++++++-------- services/inputflinger/dispatcher/InputDispatcher.h | 2 + 2 files changed, 57 insertions(+), 30 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a239723652..a21d1ebd2d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -620,6 +620,33 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } +/** + * Return true if the events preceding this incoming motion event should be dropped + * Return false otherwise (the default behaviour) + */ +bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { + bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); + if (isPointerDownEvent && + mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && + mInputTargetWaitApplicationToken != nullptr) { + int32_t displayId = motionEntry.displayId; + int32_t x = static_cast( + motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = static_cast( + motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); + if (touchedWindowHandle != nullptr && + touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { + // User touched a different application than the one we are waiting on. + // Flag the event, and start pruning the input queue. + ALOGI("Pruning input queue because user touched a different application"); + return true; + } + } + return false; +} + bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.empty(); mInboundQueue.push_back(entry); @@ -653,32 +680,18 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { // decides to touch a window in a different application. // If the application takes too long to catch up then we drop all events preceding // the touch into the other window. - MotionEntry* motionEntry = static_cast(entry); - if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && - (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && - mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && - mInputTargetWaitApplicationToken != nullptr) { - int32_t displayId = motionEntry->displayId; - int32_t x = - int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = - int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp touchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y); - if (touchedWindowHandle != nullptr && - touchedWindowHandle->getApplicationToken() != - mInputTargetWaitApplicationToken) { - // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - mNextUnblockedEvent = motionEntry; - needWake = true; - } + if (shouldPruneInboundQueueLocked(static_cast(*entry))) { + mNextUnblockedEvent = entry; + needWake = true; } break; } - case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::FOCUS: { + LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked"); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { // nothing to do break; } @@ -980,9 +993,24 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset } void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { + if (mPendingEvent != nullptr) { + // Move the pending event to the front of the queue. This will give the chance + // for the pending event to get dispatched to the newly focused window + mInboundQueue.push_front(mPendingEvent); + mPendingEvent = nullptr; + } + FocusEntry* focusEntry = new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus); - enqueueInboundEventLocked(focusEntry); + + // This event should go to the front of the queue, but behind all other focus events + // Find the last focus event, and insert right after it + std::deque::reverse_iterator it = + std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(), + [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; }); + + // Maintain the order of focus events. Insert the entry after all other focus events. + mInboundQueue.insert(it.base(), focusEntry); } void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) { @@ -2108,15 +2136,12 @@ std::string InputDispatcher::getApplicationWindowLabel( const sp& windowHandle) { if (applicationHandle != nullptr) { if (windowHandle != nullptr) { - std::string label(applicationHandle->getName()); - label += " - "; - label += windowHandle->getName(); - return label; + return applicationHandle->getName() + " - " + windowHandle->getName(); } else { return applicationHandle->getName(); } } else if (windowHandle != nullptr) { - return windowHandle->getName(); + return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName(); } else { return ""; } @@ -4620,7 +4645,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c } DispatchEntry* dispatchEntry = *dispatchEntryIt; - nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { std::string msg = StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", @@ -4644,7 +4669,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c } // Dequeue the event and start the next cycle. - // Note that because the lock might have been released, it is possible that the + // Because the lock might have been released, it is possible that the // contents of the wait queue to have been drained, so we need to double-check // a few things. dispatchEntryIt = connection->findWaitQueueEntry(seq); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d122282fbe..89b5089e49 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -345,6 +345,8 @@ private: bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); + bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); + // Contains the last window which received a hover event. sp mLastHoverWindowHandle GUARDED_BY(mLock); -- cgit v1.2.3-59-g8ed1b From ffaa2b163055c644dec7c2c681ed8b608e48f6b9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 May 2020 21:43:02 -0700 Subject: Add basic ANR test To reduce the delta of the upcoming ANR refactor, add a basic ANR test here. This will also help highlight the difference in behaviour from the current code to the new code. To cause an ANR today, the socket needs to be blocked, which means that we need to send ~ 50 events to the unresponsive window. These workarounds will be removed when the ANRs are refactored. Bug: 143459140 Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*InputDispatcherSingleWindowAnr*" --gtest_repeat=1000 --gtest_break_on_failure Change-Id: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e Merged-In: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e --- include/input/InputApplication.h | 5 + include/input/InputWindow.h | 5 + services/inputflinger/dispatcher/Entry.cpp | 6 + services/inputflinger/dispatcher/Entry.h | 2 + .../inputflinger/dispatcher/InputDispatcher.cpp | 81 +++---- services/inputflinger/dispatcher/InputDispatcher.h | 6 +- .../inputflinger/tests/InputDispatcher_test.cpp | 240 ++++++++++++++++++--- 7 files changed, 274 insertions(+), 71 deletions(-) (limited to 'services/inputflinger') diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 7f04611309..86de394a31 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -61,6 +61,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + inline sp getApplicationToken() const { return mInfo.token; } diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c5e56fd91f..2dac5b62a7 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -222,6 +222,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index a1eb0079df..21c8ae165d 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -102,6 +102,12 @@ EventEntry::~EventEntry() { releaseInjectionState(); } +std::string EventEntry::getDescription() const { + std::string result; + appendDescription(result); + return result; +} + void EventEntry::release() { refCount -= 1; if (refCount == 0) { diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ab481bd411..a135409365 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -83,6 +83,8 @@ struct EventEntry { virtual void appendDescription(std::string& msg) const = 0; + std::string getDescription() const; + protected: EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a21d1ebd2d..e6e3347ae9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,7 +78,7 @@ namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. -constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec +constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s; // Amount of time to allow for all pending events to be processed when an app switch // key is on the way. This is used to preempt input dispatch and drop input events @@ -1295,11 +1295,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - } - nsecs_t timeout; + ALOGI("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + std::chrono::nanoseconds timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != nullptr) { @@ -1311,7 +1309,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutTime = currentTime + timeout.count(); mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationToken.clear(); @@ -1353,10 +1351,10 @@ void InputDispatcher::removeWindowByTokenLocked(const sp& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp& inputConnectionToken) { - if (newTimeout > 0) { + nsecs_t timeoutExtension, const sp& inputConnectionToken) { + if (timeoutExtension > 0) { // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; + mInputTargetWaitTimeoutTime = now() + timeoutExtension; } else { // Give up. mInputTargetWaitTimeoutExpired = true; @@ -4048,11 +4046,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const int32_t displayId = it.first; const sp& applicationHandle = it.second; dump += StringPrintf(INDENT2 "displayId=%" PRId32 - ", name='%s', dispatchingTimeout=%0.3fms\n", + ", name='%s', dispatchingTimeout=%" PRId64 "ms\n", displayId, applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / - 1000000.0); + ns2ms(applicationHandle + ->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) + .count())); } } else { dump += StringPrintf(INDENT "FocusedApplications: \n"); @@ -4132,9 +4131,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 + "ms\n", windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + ns2ms(windowInfo->dispatchingTimeout)); } } else { dump += INDENT2 "Windows: \n"; @@ -4167,7 +4167,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "RecentQueue: \n"; @@ -4178,8 +4178,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "PendingEvent:\n"; dump += INDENT2; mPendingEvent->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", + ns2ms(currentTime - mPendingEvent->eventTime)); } else { dump += INDENT "PendingEvent: \n"; } @@ -4190,7 +4190,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "InboundQueue: \n"; @@ -4225,9 +4225,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 + "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime)); } } else { dump += INDENT3 "OutboundQueue: \n"; @@ -4240,10 +4241,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", + "age=%" PRId64 "ms, wait=%" PRId64 "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime), + ns2ms(currentTime - entry->deliveryTime)); } } else { dump += INDENT3 "WaitQueue: \n"; @@ -4254,16 +4255,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); + dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", + ns2ms(mAppSwitchDueTime - now())); } else { dump += INDENT "AppSwitch: not pending\n"; } dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); - dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); + dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", + ns2ms(mConfig.keyRepeatTimeout)); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector& monitors) { @@ -4365,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i return BAD_VALUE; } - [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); - ALOG_ASSERT(removed); + removeConnectionLocked(connection); mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { @@ -4468,7 +4468,7 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) { +sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; } @@ -4483,6 +4483,10 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn return nullptr; } +void InputDispatcher::removeConnectionLocked(const sp& connection) { + removeByValue(mConnectionsByFd, connection); +} + void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { @@ -4587,12 +4591,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); - nsecs_t newTimeout = + const nsecs_t timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token); + resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4647,11 +4651,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); } reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 89b5089e49..ff7be87609 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -197,6 +197,11 @@ private: // All registered connections mapped by channel file descriptor. std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); + sp getConnectionLocked(const sp& inputConnectionToken) const + REQUIRES(mLock); + + void removeConnectionLocked(const sp& connection) REQUIRES(mLock); + struct IBinderHash { std::size_t operator()(const sp& b) const { return std::hash{}(b.get()); @@ -209,7 +214,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - sp getConnectionLocked(const sp& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f33cc65c2a..13e835427f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,12 +17,14 @@ #include "../dispatcher/InputDispatcher.h" #include +#include #include #include #include #include #include +#include #include #include @@ -119,6 +121,33 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + // This function must be called soon after the expected ANR timer starts, + // because we are also checking how much time has passed. + void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, + const sp& expectedApplication, + const sp& expectedToken) { + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::unique_lock lock(mLock); + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + android::base::ScopedLockAssertion assumeLocked(mLock); + + // If there is an ANR, Dispatcher won't be idle because there are still events + // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle + // before checking if ANR was called. + // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide + // it some time to act. 100ms seems reasonable. + mNotifyAnr.wait_for(lock, timeToWait, + [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + ASSERT_TRUE(mNotifyAnrWasCalled); + // Ensure that the ANR didn't get raised too early. We can't be too strict here because + // the dispatcher started counting before this function was called + ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for + mNotifyAnrWasCalled = false; + ASSERT_EQ(expectedApplication, mLastAnrApplication); + ASSERT_EQ(expectedToken, mLastAnrWindowToken); + } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { mConfig.keyRepeatTimeout = timeout; mConfig.keyRepeatDelay = delay; @@ -131,14 +160,26 @@ private: sp mOnPointerDownToken GUARDED_BY(mLock); std::optional mLastNotifySwitch GUARDED_BY(mLock); + // ANR handling + bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; + sp mLastAnrApplication GUARDED_BY(mLock); + sp mLastAnrWindowToken GUARDED_BY(mLock); + std::condition_variable mNotifyAnr; + std::chrono::nanoseconds mAnrTimeout = 0ms; + virtual void notifyConfigurationChanged(nsecs_t when) override { std::scoped_lock lock(mLock); mConfigurationChangedTime = when; } - virtual nsecs_t notifyAnr(const sp&, const sp&, - const std::string&) override { - return 0; + virtual nsecs_t notifyAnr(const sp& application, + const sp& windowToken, const std::string&) override { + std::scoped_lock lock(mLock); + mLastAnrApplication = application; + mLastAnrWindowToken = windowToken; + mNotifyAnrWasCalled = true; + mNotifyAnr.notify_all(); + return mAnrTimeout.count(); } virtual void notifyInputChannelBroken(const sp&) override {} @@ -309,6 +350,20 @@ protected: mFakePolicy.clear(); mDispatcher.clear(); } + + /** + * Used for debugging when writing the test + */ + void dumpDispatcherState() { + std::string dump; + mDispatcher->dump(dump); + std::stringstream ss(dump); + std::string to; + + while (std::getline(ss, to, '\n')) { + ALOGE("%s", to.c_str()); + } + } }; @@ -502,13 +557,20 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: - FakeApplicationHandle() {} + FakeApplicationHandle() { + mInfo.name = "Fake Application"; + mInfo.token = new BBinder(); + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + } virtual ~FakeApplicationHandle() {} virtual bool updateInfo() override { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } + + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } }; class FakeInputReceiver { @@ -519,6 +581,20 @@ public: } InputEvent* consume() { + InputEvent* event; + std::optional consumeSeq = receiveEvent(&event); + if (!consumeSeq) { + return nullptr; + } + finishEvent(*consumeSeq); + return event; + } + + /** + * Receive an event without acknowledging it. + * Return the sequence number that could later be used to send finished signal. + */ + std::optional receiveEvent(InputEvent** outEvent = nullptr) { uint32_t consumeSeq; InputEvent* event; @@ -535,23 +611,29 @@ public: if (status == WOULD_BLOCK) { // Just means there's no event available. - return nullptr; + return std::nullopt; } if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return nullptr; + return std::nullopt; } if (event == nullptr) { ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - return nullptr; + return std::nullopt; } - - status = mConsumer->sendFinishedSignal(consumeSeq, true); - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + if (outEvent != nullptr) { + *outEvent = event; } - return event; + return consumeSeq; + } + + /** + * To be used together with "receiveEvent" to complete the consumption of an event. + */ + void finishEvent(uint32_t consumeSeq) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, @@ -668,6 +750,10 @@ public: void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -740,6 +826,19 @@ public: expectedFlags); } + std::optional receiveEvent() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::nullopt; + } + return mInputReceiver->receiveEvent(); + } + + void finishEvent(uint32_t sequenceNum) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->finishEvent(sequenceNum); + } + InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; @@ -765,16 +864,15 @@ private: std::atomic FakeWindowHandle::sId{1}; -static int32_t injectKeyDown(const sp& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, + int32_t displayId = ADISPLAY_ID_NONE) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, - AMETA_NONE, - /* repeatCount */ 0, currentTime, currentTime); + INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + repeatCount, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -783,10 +881,16 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionEvent(const sp& dispatcher, int32_t action, - int32_t source, int32_t displayId, int32_t x, int32_t y, - int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, - int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { +static int32_t injectKeyDown(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); +} + +static int32_t injectMotionEvent( + const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, + const PointF& position, + const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -796,8 +900,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. @@ -806,7 +910,7 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, - /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -819,14 +923,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t static int32_t injectMotionDown(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } static int32_t injectMotionUp(const sp& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); } static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { @@ -1051,7 +1153,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // left window. This event should be dispatched to the left window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, - ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400})); windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowRight->assertNoEvents(); } @@ -2185,4 +2287,82 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } +class InputDispatcherSingleWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(20ms); + mWindow = + new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 30, 30)); + mWindow->setDispatchingTimeout(10ms); + mWindow->setFocus(true); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + mWindow.clear(); + } + +protected: + sp mApplication; + sp mWindow; + static constexpr PointF WINDOW_LOCATION = {20, 20}; + + void tapOnWindow() { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + } +}; + +// Send an event to the app and have the app not respond right away. +// Make sure that ANR is raised +TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + // Also, overwhelm the socket to make sure ANR starts + for (size_t i = 0; i < 100; i++) { + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); + } + + std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// Send a key to the app and have the app not respond right away. +TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { + // Inject a key, and don't respond - expect that ANR is called. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + std::optional sequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(sequenceNum); + + // Start ANR process by sending a 2nd key, which would trigger the check for whether + // waitQueue is empty + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 98c34a8cee40b7cebcb266c3cb66891d601ac44b Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 9 Jun 2020 15:36:34 -0700 Subject: InputDispatcher: Don't occlude windows with the same PID. First a review of history. In previous releases there was no PID consideration in occlusion detection. In a recent patch set we introduced sending InputChannel-less surfaces from SurfaceFlinger to InputDispatcher for more complete occlusion detection. As part of this process we introduced the ownerPid check that is modified in this CL. We added this because otherwise SurfaceView would begin occluding their parents, and this would be a compatibility break (and also not very useful). When adding this line we also added the token == null check. This was to preserve a previous semantic where windows from the same ownerPid would occlude eachother in previous releases. There was no known dependence on this semantic, it was just easier to add the line to preserve the old behavior than think about changing it. In light of the linked bug, we see a use case for changing it. On further consideration, it seems not a useful thing to preserve, as there are no security boundaries within a process and the ability for an app to truly depend on this behavior is very small. Bug: 157772682 Test: Existing tests pass Change-Id: Ife2a5a9fce35e04bb794d79b3a1b875859deb8dd --- services/inputflinger/dispatcher/InputDispatcher.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 8dddd6dd16..4affdad903 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1991,14 +1991,9 @@ static bool canBeObscuredBy(const sp& windowHandle, auto otherInfo = otherHandle->getInfo(); if (!otherInfo->visible) { return false; - } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) { - // In general, if ownerPid is the same we don't want to generate occlusion - // events. This line is now necessary since we are including all Surfaces - // in occlusion calculation, so if we didn't check PID like this SurfaceView - // would occlude their parents. On the other hand before we started including - // all surfaces in occlusion calculation and had this line, we would count - // windows with an input channel from the same PID as occluding, and so we - // preserve this behavior with the getToken() == null check. + } else if (info->ownerPid == otherInfo->ownerPid) { + // If ownerPid is the same we don't generate occlusion events as there + // is no in-process security boundary. return false; } else if (otherInfo->isTrustedOverlay()) { return false; -- cgit v1.2.3-59-g8ed1b From e0fb6bdd6cc08619fb6889a37d8a03b4b4bce275 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 13 Apr 2020 11:40:37 -0700 Subject: Make addGestureMonitors static The function is already static, but it's marked as if part of InputDispatcher. Make it clearly static by moving out of InputDispatcher.h Bug: 142017994 Test: none (this is a cherry pick) Change-Id: I098e86453b7bb8261e08f7fce671db173e2136b0 Merged-In: I098e86453b7bb8261e08f7fce671db173e2136b0 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 50 ++++++++++------------ services/inputflinger/dispatcher/InputDispatcher.h | 5 +-- 2 files changed, 23 insertions(+), 32 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 8dddd6dd16..2bc198c1bf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -328,6 +328,18 @@ static std::unique_ptr createDispatchEntry(const InputTarget& inp return dispatchEntry; } +static void addGestureMonitors(const std::vector& monitors, + std::vector& outTouchedMonitors, float xOffset = 0, + float yOffset = 0) { + if (monitors.empty()) { + return; + } + outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size()); + for (const Monitor& monitor : monitors) { + outTouchedMonitors.emplace_back(monitor, xOffset, yOffset); + } +} + static std::array getRandomKey() { std::array key; if (RAND_bytes(key.data(), key.size()) != 1) { @@ -752,7 +764,7 @@ sp InputDispatcher::findTouchedWindowAtLocked(int32_t display } std::vector InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector>& portalWindows) { + int32_t displayId, const std::vector>& portalWindows) const { std::vector touchedMonitors; std::vector monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); @@ -766,18 +778,6 @@ std::vector InputDispatcher::findTouchedGestureMonitorsLocked( return touchedMonitors; } -void InputDispatcher::addGestureMonitors(const std::vector& monitors, - std::vector& outTouchedMonitors, - float xOffset, float yOffset) { - if (monitors.empty()) { - return; - } - outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size()); - for (const Monitor& monitor : monitors) { - outTouchedMonitors.emplace_back(monitor, xOffset, yOffset); - } -} - void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) { const char* reason; switch (dropReason) { @@ -1508,11 +1508,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { - if (DEBUG_FOCUS) { - ALOGD("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, - displayId); - } + ALOGI("Dropping event because a pointer for a different device is already down " + "in display %" PRId32, + displayId); // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_FAILED; switchedDevice = false; @@ -1526,11 +1524,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { - if (DEBUG_FOCUS) { - ALOGI("Dropping move event because a pointer for a different device is already active " - "in display %" PRId32, - displayId); - } + ALOGI("Dropping move event because a pointer for a different device is already active " + "in display %" PRId32, + displayId); // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; switchedDevice = false; @@ -1714,11 +1710,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); if (!haveForegroundWindow && !hasGestureMonitor) { - if (DEBUG_FOCUS) { - ALOGD("Dropping event because there is no touched foreground window in display " - "%" PRId32 " or gesture monitor to receive it.", - displayId); - } + ALOGI("Dropping event because there is no touched foreground window in display " + "%" PRId32 " or gesture monitor to receive it.", + displayId); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index ff7be87609..df5b59135a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -378,11 +378,8 @@ private: nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); std::vector findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector>& portalWindows) + int32_t displayId, const std::vector>& portalWindows) const REQUIRES(mLock); - void addGestureMonitors(const std::vector& monitors, - std::vector& outTouchedMonitors, float xOffset = 0, - float yOffset = 0); void addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) -- cgit v1.2.3-59-g8ed1b From bde3d9edd77575cb337bc7c407bc6c24a6636485 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Mar 2020 19:05:54 -0700 Subject: Replace KeyedVector with unordered_map in dispatcher To modernize the code base and increase readability, use std::unordered_map instead of KeyedVector inside InputDispatcher. This helps understand the code flow, and allows some foreach expressions, thus removing shadow variables. Bug: 142017994 Test: presubmit Change-Id: I969fc03dc38e258e9f3d0f2e60c9534515ce2926 Merged-In: I969fc03dc38e258e9f3d0f2e60c9534515ce2926 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 72 +++++++++++----------- services/inputflinger/dispatcher/InputDispatcher.h | 10 +-- 2 files changed, 42 insertions(+), 40 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 2bc198c1bf..2ee3ebe0e0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1197,9 +1197,10 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); if (isPointerEvent) { - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId); - if (stateIndex >= 0) { - const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex); + std::unordered_map::iterator it = + mTouchStatesByDisplay.find(entry->displayId); + if (it != mTouchStatesByDisplay.end()) { + const TouchState& state = it->second; if (!state.portalWindows.empty()) { // The event has gone through these portal windows, so we add monitoring targets of // the corresponding displays as well. @@ -1344,8 +1345,8 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } void InputDispatcher::removeWindowByTokenLocked(const sp& token) { - for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { - TouchState& state = mTouchStatesByDisplay.editValueAt(d); + for (std::pair& pair : mTouchStatesByDisplay) { + TouchState& state = pair.second; state.removeWindowByToken(token); } } @@ -1488,9 +1489,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // This state is always reset at the end of this function, so if we don't find state // for the specified display then our initial state will be empty. const TouchState* oldState = nullptr; - ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (oldStateIndex >= 0) { - oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex); + std::unordered_map::iterator oldStateIt = + mTouchStatesByDisplay.find(displayId); + if (oldStateIt != mTouchStatesByDisplay.end()) { + oldState = &(oldStateIt->second); mTempTouchState.copyFrom(*oldState); } @@ -1876,13 +1878,9 @@ Failed: // state was only valid for this one action. if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { if (mTempTouchState.displayId >= 0) { - if (oldStateIndex >= 0) { - mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState); - } else { - mTouchStatesByDisplay.add(displayId, mTempTouchState); - } - } else if (oldStateIndex >= 0) { - mTouchStatesByDisplay.removeItemsAt(oldStateIndex); + mTouchStatesByDisplay[displayId] = mTempTouchState; + } else { + mTouchStatesByDisplay.erase(displayId); } } @@ -3037,7 +3035,7 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 if (newKeyCode != AKEYCODE_UNKNOWN) { std::scoped_lock _l(mLock); struct KeyReplacement replacement = {keyCode, deviceId}; - mReplacedKeys.add(replacement, newKeyCode); + mReplacedKeys[replacement] = newKeyCode; keyCode = newKeyCode; metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); } @@ -3047,10 +3045,10 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 // even if the modifier was released between the down and the up events. std::scoped_lock _l(mLock); struct KeyReplacement replacement = {keyCode, deviceId}; - ssize_t index = mReplacedKeys.indexOfKey(replacement); - if (index >= 0) { - keyCode = mReplacedKeys.valueAt(index); - mReplacedKeys.removeItemsAt(index); + auto replacementIt = mReplacedKeys.find(replacement); + if (replacementIt != mReplacedKeys.end()) { + keyCode = replacementIt->second; + mReplacedKeys.erase(replacementIt); metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); } } @@ -3732,9 +3730,10 @@ void InputDispatcher::setInputWindowsLocked( } } - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex >= 0) { - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + std::unordered_map::iterator stateIt = + mTouchStatesByDisplay.find(displayId); + if (stateIt != mTouchStatesByDisplay.end()) { + TouchState& state = stateIt->second; for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { @@ -3949,8 +3948,8 @@ bool InputDispatcher::transferTouchFocus(const sp& fromToken, const sp< } bool found = false; - for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { - TouchState& state = mTouchStatesByDisplay.editValueAt(d); + for (std::pair& pair : mTouchStatesByDisplay) { + TouchState& state = pair.second; for (size_t i = 0; i < state.windows.size(); i++) { const TouchedWindow& touchedWindow = state.windows[i]; if (touchedWindow.windowHandle == fromWindowHandle) { @@ -4064,10 +4063,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "FocusedWindows: \n"); } - if (!mTouchStatesByDisplay.isEmpty()) { + if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); - for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) { - const TouchState& state = mTouchStatesByDisplay.valueAt(i); + for (const std::pair& pair : mTouchStatesByDisplay) { + const TouchState& state = pair.second; dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", state.displayId, toString(state.down), toString(state.split), state.deviceId, state.source); @@ -4191,12 +4190,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "InboundQueue: \n"; } - if (!mReplacedKeys.isEmpty()) { + if (!mReplacedKeys.empty()) { dump += INDENT "ReplacedKeys:\n"; - for (size_t i = 0; i < mReplacedKeys.size(); i++) { - const KeyReplacement& replacement = mReplacedKeys.keyAt(i); - int32_t newKeyCode = mReplacedKeys.valueAt(i); - dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i, + for (const std::pair& pair : mReplacedKeys) { + const KeyReplacement& replacement = pair.first; + int32_t newKeyCode = pair.second; + dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n", replacement.keyCode, replacement.deviceId, newKeyCode); } } else { @@ -4413,13 +4412,14 @@ status_t InputDispatcher::pilferPointers(const sp& token) { } int32_t displayId = foundDisplayId.value(); - ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); - if (stateIndex < 0) { + std::unordered_map::iterator stateIt = + mTouchStatesByDisplay.find(displayId); + if (stateIt == mTouchStatesByDisplay.end()) { ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId); return BAD_VALUE; } - TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); + TouchState& state = stateIt->second; std::optional foundDeviceId; for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index df5b59135a..81682c5a1a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -255,12 +255,14 @@ private: bool operator==(const KeyReplacement& rhs) const { return keyCode == rhs.keyCode && deviceId == rhs.deviceId; } - bool operator<(const KeyReplacement& rhs) const { - return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId; + }; + struct KeyReplacementHash { + size_t operator()(const KeyReplacement& key) const { + return std::hash()(key.keyCode) ^ (std::hash()(key.deviceId) << 1); } }; // Maps the key code replaced, device id tuple to the key code it was replaced with - KeyedVector mReplacedKeys GUARDED_BY(mLock); + std::unordered_map mReplacedKeys GUARDED_BY(mLock); // Process certain Meta + Key combinations void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode, int32_t& metaState); @@ -308,7 +310,7 @@ private: std::unordered_map> mFocusedWindowHandlesByDisplay GUARDED_BY(mLock); - KeyedVector mTouchStatesByDisplay GUARDED_BY(mLock); + std::unordered_map mTouchStatesByDisplay GUARDED_BY(mLock); TouchState mTempTouchState GUARDED_BY(mLock); // Focused applications. -- cgit v1.2.3-59-g8ed1b From 1ea3cf10aa1f5258eb98b34264ac94eb004b2239 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 24 Mar 2020 19:50:03 -0700 Subject: Make tempTouchState a local variable Currently, mTempTouchState is a class variable, and it is confusing when it is being modified in functions such as findTouchedWindowAt. This variable is only used inside findTouchedWindowTargets, so convert it to a local variable. This will make it easier to keep track of its contents. Bug: 142017994 Test: none (this is a cherry-pick) Change-Id: I8b4f45eadf0ba05e5979de39f78ba81fe79bce31 Merged-In: I8b4f45eadf0ba05e5979de39f78ba81fe79bce31 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 134 +++++++++++---------- services/inputflinger/dispatcher/InputDispatcher.h | 2 +- 2 files changed, 71 insertions(+), 65 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 2ee3ebe0e0..46f6f44612 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -647,7 +647,8 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = static_cast( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); + sp touchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y, nullptr); if (touchedWindowHandle != nullptr && touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { // User touched a different application than the one we are waiting on. @@ -722,8 +723,13 @@ void InputDispatcher::addRecentEventLocked(EventEntry* entry) { } sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, - int32_t y, bool addOutsideTargets, + int32_t y, TouchState* touchState, + bool addOutsideTargets, bool addPortalWindows) { + if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) { + LOG_ALWAYS_FATAL( + "Must provide a valid touch state if adding portal windows or outside targets"); + } // Traverse windows from front to back to find touched window. const std::vector> windowHandles = getWindowHandlesLocked(displayId); for (const sp& windowHandle : windowHandles) { @@ -742,9 +748,9 @@ sp InputDispatcher::findTouchedWindowAtLocked(int32_t display portalToDisplayId != displayId) { if (addPortalWindows) { // For the monitoring channels of the display. - mTempTouchState.addPortalWindow(windowHandle); + touchState->addPortalWindow(windowHandle); } - return findTouchedWindowAtLocked(portalToDisplayId, x, y, + return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState, addOutsideTargets, addPortalWindows); } // Found window. @@ -753,9 +759,9 @@ sp InputDispatcher::findTouchedWindowAtLocked(int32_t display } if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) { - mTempTouchState.addOrUpdateWindow(windowHandle, - InputTarget::FLAG_DISPATCH_AS_OUTSIDE, - BitSet32(0)); + touchState->addOrUpdateWindow(windowHandle, + InputTarget::FLAG_DISPATCH_AS_OUTSIDE, + BitSet32(0)); } } } @@ -1485,21 +1491,22 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; sp newHoverWindowHandle; - // Copy current touch state into mTempTouchState. - // This state is always reset at the end of this function, so if we don't find state - // for the specified display then our initial state will be empty. + // Copy current touch state into tempTouchState. + // This state will be used to update mTouchStatesByDisplay at the end of this function. + // If no state for the specified display exists, then our initial state will be empty. const TouchState* oldState = nullptr; + TouchState tempTouchState; std::unordered_map::iterator oldStateIt = mTouchStatesByDisplay.find(displayId); if (oldStateIt != mTouchStatesByDisplay.end()) { oldState = &(oldStateIt->second); - mTempTouchState.copyFrom(*oldState); + tempTouchState.copyFrom(*oldState); } - bool isSplit = mTempTouchState.split; - bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 && - (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source || - mTempTouchState.displayId != displayId); + bool isSplit = tempTouchState.split; + bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 && + (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source || + tempTouchState.displayId != displayId); bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); @@ -1509,7 +1516,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { + if (switchedDevice && tempTouchState.down && !down && !isHoverAction) { ALOGI("Dropping event because a pointer for a different device is already down " "in display %" PRId32, displayId); @@ -1519,11 +1526,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, wrongDevice = true; goto Failed; } - mTempTouchState.reset(); - mTempTouchState.down = down; - mTempTouchState.deviceId = entry.deviceId; - mTempTouchState.source = entry.source; - mTempTouchState.displayId = displayId; + tempTouchState.reset(); + tempTouchState.down = down; + tempTouchState.deviceId = entry.deviceId; + tempTouchState.source = entry.source; + tempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " @@ -1552,11 +1559,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; sp newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/, - true /*addPortalWindows*/); + findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, + isDown /*addOutsideTargets*/, true /*addPortalWindows*/); std::vector newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows) + ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows) : std::vector{}; // Figure out whether splitting will be allowed for this window. @@ -1573,7 +1580,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { // Try to assign the pointer to the first foreground window we find, if there is one. - newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); + newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { @@ -1609,15 +1616,15 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, uint32_t pointerId = entry.pointerProperties[pointerIndex].id; pointerIds.markBit(pointerId); } - mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } - mTempTouchState.addGestureMonitors(newGestureMonitors); + tempTouchState.addGestureMonitors(newGestureMonitors); } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (!mTempTouchState.down) { + if (!tempTouchState.down) { if (DEBUG_FOCUS) { ALOGD("Dropping event because the pointer is not down or we previously " "dropped the pointer down event in display %" PRId32, @@ -1629,14 +1636,14 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && - mTempTouchState.isSlippery()) { + tempTouchState.isSlippery()) { int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); sp oldTouchedWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); + tempTouchState.getFirstForegroundWindowHandle(); sp newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y); + findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { if (DEBUG_FOCUS) { @@ -1645,9 +1652,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, newTouchedWindowHandle->getName().c_str(), displayId); } // Make a slippery exit from the old window. - mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, - BitSet32(0)); + tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + BitSet32(0)); // Make a slippery entrance into the new window. if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { @@ -1667,7 +1674,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (isSplit) { pointerIds.markBit(entry.pointerProperties[0].id); } - mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } } } @@ -1679,9 +1686,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, ALOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->getName().c_str()); #endif - mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, - BitSet32(0)); + tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); } // Let the new window know that the hover sequence is starting. @@ -1690,9 +1696,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, ALOGD("Sending hover enter event to window %s.", newHoverWindowHandle->getName().c_str()); #endif - mTempTouchState.addOrUpdateWindow(newHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, - BitSet32(0)); + tempTouchState.addOrUpdateWindow(newHoverWindowHandle, + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, + BitSet32(0)); } } @@ -1700,7 +1706,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // is at least one touched foreground window. { bool haveForegroundWindow = false; - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { haveForegroundWindow = true; if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) { @@ -1710,7 +1716,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } } - bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); + bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty(); if (!haveForegroundWindow && !hasGestureMonitor) { ALOGI("Dropping event because there is no touched foreground window in display " "%" PRId32 " or gesture monitor to receive it.", @@ -1727,16 +1733,16 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // set the policy flag that we will not reveal coordinate information to this window. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { sp foregroundWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); + tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle) { const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { sp inputWindowHandle = touchedWindow.windowHandle; if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) { - mTempTouchState.addOrUpdateWindow(inputWindowHandle, - InputTarget::FLAG_ZERO_COORDS, - BitSet32(0)); + tempTouchState.addOrUpdateWindow(inputWindowHandle, + InputTarget::FLAG_ZERO_COORDS, + BitSet32(0)); } } } @@ -1744,7 +1750,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Ensure all touched foreground windows are ready for new input. - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // Check whether the window is ready for more input. std::string reason = @@ -1766,7 +1772,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { sp foregroundWindowHandle = - mTempTouchState.getFirstForegroundWindowHandle(); + tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) { const std::vector> windowHandles = getWindowHandlesLocked(displayId); @@ -1774,7 +1780,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const InputWindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) { - mTempTouchState + tempTouchState .addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | InputTarget:: @@ -1789,19 +1795,19 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { + for (const TouchedWindow& touchedWindow : tempTouchState.windows) { addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.pointerIds, inputTargets); } - for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) { + for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) { addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, touchedMonitor.yOffset, inputTargets); } // Drop the outside or hover touch windows since we will not care about them // in the next iteration. - mTempTouchState.filterNonAsIsTouchWindows(); + tempTouchState.filterNonAsIsTouchWindows(); Failed: // Check injection permission once and for all. @@ -1835,17 +1841,17 @@ Failed: } *outConflictingPointerActions = true; } - mTempTouchState.reset(); + tempTouchState.reset(); if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mTempTouchState.deviceId = entry.deviceId; - mTempTouchState.source = entry.source; - mTempTouchState.displayId = displayId; + tempTouchState.deviceId = entry.deviceId; + tempTouchState.source = entry.source; + tempTouchState.displayId = displayId; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. - mTempTouchState.reset(); + tempTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && oldState->down) { @@ -1860,12 +1866,12 @@ Failed: int32_t pointerIndex = getMotionEventActionPointerIndex(action); uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - for (size_t i = 0; i < mTempTouchState.windows.size();) { - TouchedWindow& touchedWindow = mTempTouchState.windows[i]; + for (size_t i = 0; i < tempTouchState.windows.size();) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { touchedWindow.pointerIds.clearBit(pointerId); if (touchedWindow.pointerIds.isEmpty()) { - mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i); + tempTouchState.windows.erase(tempTouchState.windows.begin() + i); continue; } } @@ -1877,8 +1883,8 @@ Failed: // Save changes unless the action was scroll in which case the temporary touch // state was only valid for this one action. if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (mTempTouchState.displayId >= 0) { - mTouchStatesByDisplay[displayId] = mTempTouchState; + if (tempTouchState.displayId >= 0) { + mTouchStatesByDisplay[displayId] = tempTouchState; } else { mTouchStatesByDisplay.erase(displayId); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 81682c5a1a..0c3ea3674c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -191,6 +191,7 @@ private: EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); sp findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, + TouchState* touchState, bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock); @@ -311,7 +312,6 @@ private: GUARDED_BY(mLock); std::unordered_map mTouchStatesByDisplay GUARDED_BY(mLock); - TouchState mTempTouchState GUARDED_BY(mLock); // Focused applications. std::unordered_map> mFocusedApplicationHandlesByDisplay -- cgit v1.2.3-59-g8ed1b From e4623041f91c3fac633fc0f1803bc3a7c08c6eb9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 25 Mar 2020 16:16:40 -0700 Subject: Report ANR when waited for longer than the timeout Previously, when the touched window was unresponsive, we skipped adding the gesture monitors to the touch targets. That means, when an app has ANR, gesture nav stops working, and the phone feels frozen, until the ANR dialog comes up. Another failure mode was a stuck pending event. If there is no focused window, and we have a pending key event (or any focused event), we will be waiting for a focused window to appear. That would still prevent gesture monitors from receiving further touch events. In this solution, we do not add unresponsive windows to the list of targets, but we still proceed with handling the event, meaning that the app won't get a new DOWN when it is unresponsive, but the event would still go to the gesture monitors. That change, in isolation, would also break ANR for the case when the app loses focus. To maintain the ANR functionality, we extend the ANR detection mechanism to all connections. Now, every connection is eligible to receive an ANR, even if it's a gesture monitor. We expect everything in the system to be responsive within reasonable timeouts. We also change the handling of extended ANR timeouts coming from policy. Today, the behaviour is as follows: 1. If the policy says "wait longer", then we do nothing and just keep waiting 2. If the policy says "abort", then we send the cancel events and remove the window from the window list. The "abort" approach seems incorrect, because the policy will probably not register the existing inputchannel/connection with a new window. If the user does click "close app" when the ANR dialog appears, we will anyways receive "window removed" event via setInputWindows, and will clean up the connection that way. So we don't really need to do anything other than sending "cancel events" to the window. The policy now for sending events to unresponsive windows becomes: 1. If the unresponsive window is touched, the new touch stream does not go to the window. It will go to all other places, though. 2. If the unresponsive window receives a focused event, the event still gets queued for the unresponsive window to handle. For improved ANR performance, the ANR detection is now done by introducing a helper data structure, a multiset of the timeout times. Whenever we send an event to a connection, we will calculate the time that this event will cause a timeout. We will then add this time to the multiset of times. When we check for ANR inside dispatchOnce, we will only access the smallest (soonest) timeout inside the multiset. If the current time is before this smallest timeout time, then everything is normal, and we move on. This would cost O(1). If the time is past the timeout time, it means a connection is unresponsive. In this case, we take the closest in time unresponsive entry. That entry already has the connection token, for convenience. We then raise an ANR on that connection. The entries are removed from the multiset in several cases: 1. When we receive a 'finished' signal on an entry for a specific connection 2. When the connection becomes unresponsive and we raise ANR. In that case, no need to keep checking on the same connection. Once case 1. applies to that connection, entries from that connection will again become eligible for being added to the multiset. 3. When we reset and drop everything. 4. When we cannot find a connection for an entry in the multiset, we will drop all entries from that connection. If we report ANR for an app, we do not report the second ANR until the waitQueue becomes healthy first and then becomes clogged again. If we have a focused application, but no window has focus, then nothing will happen for pointer events. They will keep going to the touched window as normal. When we receive the first focused event, however, we will start a timer. If there is no focused window added by that time, we will send an ANR for that application. This logic should be moved into WM later, because from the input perspective, it is legitimate to have an application without a focused window. This would also allow WM to remove the "setFocusedApplication" call. Bug: 143459140 Test: use the test app from the bug. The app sleeps for 10 seconds when the button is clicked. Click the button, and try to use gesture nav several times. Observe that gesture nav continues to work even after app has ANR. Observe that ANR is reported even after interacting with gesture nav. Test: Click on the app multiple times (to clog up the queue), and then wait for a long time, even after the ANR dialog shows up. Then click "wait" to not close the app. Then click again on the app. Observe that the anr dialog appears after a while. This indicates that at some point, the app processed all events, and then became eligible for anr again. Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests Test: create an app that sets "FLAG_NOT_FOCUSABLE" on its only window. Launch the app and interact with it by touch. Notice that the app does not ANR. Then, send the back key to the app (using the back gesture). Notice that in 5 seconds, we receive an ANR for this app. While the BACK key is queued up for this app, the gesture nav continues to work and the notification shade can still be pulled down. Change-Id: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf Merged-In: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf --- include/input/Input.h | 4 + libs/input/Input.cpp | 31 + services/inputflinger/dispatcher/Android.bp | 1 + services/inputflinger/dispatcher/AnrTracker.cpp | 73 ++ services/inputflinger/dispatcher/AnrTracker.h | 60 ++ services/inputflinger/dispatcher/Connection.cpp | 3 +- services/inputflinger/dispatcher/Connection.h | 7 +- services/inputflinger/dispatcher/Entry.cpp | 36 +- services/inputflinger/dispatcher/Entry.h | 4 + .../inputflinger/dispatcher/InputDispatcher.cpp | 665 ++++++++++-------- services/inputflinger/dispatcher/InputDispatcher.h | 86 ++- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/AnrTracker_test.cpp | 167 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 750 +++++++++++++++++++-- 14 files changed, 1499 insertions(+), 389 deletions(-) create mode 100644 services/inputflinger/dispatcher/AnrTracker.cpp create mode 100644 services/inputflinger/dispatcher/AnrTracker.h create mode 100644 services/inputflinger/tests/AnrTracker_test.cpp (limited to 'services/inputflinger') diff --git a/include/input/Input.h b/include/input/Input.h index 9e47318203..54b4e5a737 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -462,6 +462,8 @@ public: nsecs_t eventTime); void initialize(const KeyEvent& from); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mFlags; @@ -725,6 +727,8 @@ public: static const char* getLabel(int32_t axis); static int32_t getAxisFromLabel(const char* label); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mActionButton; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c2437673df..31aa685391 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -169,6 +169,18 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } +const char* KeyEvent::actionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return "UNKNOWN"; +} // --- PointerCoords --- @@ -678,6 +690,25 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +const char* MotionEvent::actionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_CANCEL: + return "CANCEL"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return "UNKNOWN"; +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b242eec465..d29d8dfda3 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -22,6 +22,7 @@ cc_library_headers { filegroup { name: "libinputdispatcher_sources", srcs: [ + "AnrTracker.cpp", "Connection.cpp", "Entry.cpp", "InjectionState.cpp", diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp new file mode 100644 index 0000000000..c3f611e7db --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AnrTracker.h" + +namespace android::inputdispatcher { + +template +static T max(const T& a, const T& b) { + return a < b ? b : a; +} + +void AnrTracker::insert(nsecs_t timeoutTime, sp token) { + mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token))); +} + +/** + * Erase a single entry only. If there are multiple duplicate entries + * (same time, same connection), then only remove one of them. + */ +void AnrTracker::erase(nsecs_t timeoutTime, const sp& token) { + auto pair = std::make_pair(timeoutTime, token); + auto it = mAnrTimeouts.find(pair); + if (it != mAnrTimeouts.end()) { + mAnrTimeouts.erase(it); + } +} + +void AnrTracker::eraseToken(const sp& token) { + for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) { + if (it->second == token) { + it = mAnrTimeouts.erase(it); + } else { + ++it; + } + } +} + +bool AnrTracker::empty() const { + return mAnrTimeouts.empty(); +} + +// If empty() is false, return the time at which the next connection should cause an ANR +// If empty() is true, return LONG_LONG_MAX +nsecs_t AnrTracker::firstTimeout() const { + if (mAnrTimeouts.empty()) { + return std::numeric_limits::max(); + } + return mAnrTimeouts.begin()->first; +} + +const sp& AnrTracker::firstToken() const { + return mAnrTimeouts.begin()->second; +} + +void AnrTracker::clear() { + mAnrTimeouts.clear(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h new file mode 100644 index 0000000000..097dba5bea --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H +#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H + +#include +#include +#include + +namespace android::inputdispatcher { + +/** + * Keeps track of the times when each connection is going to ANR. + * Provides the ability to quickly find the connection that is going to cause ANR next. + */ +class AnrTracker { +public: + void insert(nsecs_t timeoutTime, sp token); + void erase(nsecs_t timeoutTime, const sp& token); + void eraseToken(const sp& token); + void clear(); + + bool empty() const; + // If empty() is false, return the time at which the next connection should cause an ANR + // If empty() is true, return LONG_LONG_MAX + nsecs_t firstTimeout() const; + // Return the token of the next connection that should cause an ANR. + // Do not call this unless empty() is false, you will encounter undefined behaviour. + const sp& firstToken() const; + +private: + // Optimization: use a multiset to keep track of the event timeouts. When an event is sent + // to the InputConsumer, we add an entry to this structure. We look at the smallest value to + // determine if any of the connections is unresponsive, and to determine when we should wake + // next for the future ANR check. + // Using a multiset helps quickly look up the next timeout due. + // + // We must use a multi-set, because it is plausible (although highly unlikely) to have entries + // from the same connection and same timestamp, but different sequence numbers. + // We are not tracking sequence numbers, and just allow duplicates to exist. + std::multiset /*connectionToken*/>> mAnrTimeouts; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index 188212bccf..f5ea563311 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -26,8 +26,7 @@ Connection::Connection(const sp& inputChannel, bool monitor, inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), - inputState(idGenerator), - inputPublisherBlocked(false) {} + inputState(idGenerator) {} Connection::~Connection() {} diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index bb3f2fee19..3b33f29dff 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -47,9 +47,10 @@ public: InputPublisher inputPublisher; InputState inputState; - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; + // True if this connection is responsive. + // If this connection is not responsive, avoid publishing more events to it until the + // application consumes some of the input. + bool responsive = true; // Queue of events that need to be published to the connection. std::deque outboundQueue; diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 21c8ae165d..fdbb1d1b55 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -28,38 +28,6 @@ using android::base::StringPrintf; namespace android::inputdispatcher { -static std::string motionActionToString(int32_t action) { - // Convert MotionEvent action to string - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; - case AMOTION_EVENT_ACTION_UP: - return "UP"; - case AMOTION_EVENT_ACTION_CANCEL: - return "CANCEL"; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; - case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string keyActionToString(int32_t action) { - // Convert KeyEvent action to string - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - return "DOWN"; - case AKEY_EVENT_ACTION_UP: - return "UP"; - case AKEY_EVENT_ACTION_MULTIPLE: - return "MULTIPLE"; - } - return StringPrintf("%" PRId32, action); -} - VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, @@ -191,7 +159,7 @@ void KeyEntry::appendDescription(std::string& msg) const { msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, keyActionToString(action).c_str(), flags, + deviceId, source, displayId, KeyEvent::actionToString(action), flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @@ -253,7 +221,7 @@ void MotionEntry::appendDescription(std::string& msg) const { "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), + deviceId, source, displayId, MotionEvent::actionToString(action), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, yPrecision, xCursorPosition, yCursorPosition); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a135409365..6b7697dde6 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -198,7 +198,11 @@ struct DispatchEntry { float globalScaleFactor; float windowXScale = 1.0f; float windowYScale = 1.0f; + // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, + // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered + // An ANR will be triggered if a response for this entry is not received by timeoutTime + nsecs_t timeoutTime; // Set to the resolved ID, action and flags when the event is enqueued. int32_t resolvedEventId; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 959eeea41e..677bf7e2ec 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -90,18 +90,17 @@ constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec -// Amount of time to allow touch events to be streamed out to a connection before requiring -// that the first event be finished. This value extends the ANR timeout by the specified -// amount. For example, if streaming is allowed to get ahead by one second relative to the -// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. -constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec - // Log a warning when an event takes longer than this to process, even if an ANR does not occur. constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; +// Additional key latency in case a connection is still processing some motion events. +// This will help with the case when a user touched a button that opens a new window, +// and gives us the chance to dispatch the key to this new window. +constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; + // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -409,8 +408,7 @@ InputDispatcher::InputDispatcher(const sp& polic // To avoid leaking stack in case that call never comes, and for tests, // initialize it here anyways. mInTouchMode(true), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { + mFocusedDisplayId(ADISPLAY_ID_DEFAULT) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -470,6 +468,11 @@ void InputDispatcher::dispatchOnce() { nextWakeupTime = LONG_LONG_MIN; } + // If we are still waiting for ack on some events, + // we might have to wake up earlier to check if an app is anr'ing. + const nsecs_t nextAnrCheck = processAnrsLocked(); + nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck); + // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events if (nextWakeupTime == LONG_LONG_MAX) { @@ -483,6 +486,55 @@ void InputDispatcher::dispatchOnce() { mLooper->pollOnce(timeoutMillis); } +/** + * Check if any of the connections' wait queues have events that are too old. + * If we waited for events to be ack'ed for more than the window timeout, raise an ANR. + * Return the time at which we should wake up next. + */ +nsecs_t InputDispatcher::processAnrsLocked() { + const nsecs_t currentTime = now(); + nsecs_t nextAnrCheck = LONG_LONG_MAX; + // Check if we are waiting for a focused window to appear. Raise ANR if waited too long + if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { + if (currentTime >= *mNoFocusedWindowTimeoutTime) { + onAnrLocked(mAwaitedFocusedApplication); + mAwaitedFocusedApplication.clear(); + return LONG_LONG_MIN; + } else { + // Keep waiting + const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime); + ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining); + nextAnrCheck = *mNoFocusedWindowTimeoutTime; + } + } + + // Check if any connection ANRs are due + nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout()); + if (currentTime < nextAnrCheck) { // most likely scenario + return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck + } + + // If we reached here, we have an unresponsive connection. + sp connection = getConnectionLocked(mAnrTracker.firstToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout()); + return nextAnrCheck; + } + connection->responsive = false; + // Stop waking up for this unresponsive connection + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + onAnrLocked(connection); + return LONG_LONG_MIN; +} + +nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp& token) { + sp window = getWindowHandleLocked(token); + if (window != nullptr) { + return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count(); + } + return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count(); +} + void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); @@ -546,9 +598,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(*mPendingEvent); } - - // Get ready to dispatch the event. - resetAnrTimeoutsLocked(); } // Now we have an event to dispatch. @@ -642,11 +691,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { * Return false otherwise (the default behaviour) */ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { - bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); - if (isPointerDownEvent && - mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && - mInputTargetWaitApplicationToken != nullptr) { + + // Optimize case where the current application is unresponsive and the user + // decides to touch a window in a different application. + // If the application takes too long to catch up then we drop all events preceding + // the touch into the other window. + if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) { int32_t displayId = motionEntry.displayId; int32_t x = static_cast( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); @@ -655,12 +707,41 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); if (touchedWindowHandle != nullptr && - touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { + touchedWindowHandle->getApplicationToken() != + mAwaitedFocusedApplication->getApplicationToken()) { // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - ALOGI("Pruning input queue because user touched a different application"); + ALOGI("Pruning input queue because user touched a different application while waiting " + "for %s", + mAwaitedFocusedApplication->getName().c_str()); return true; } + + // Alternatively, maybe there's a gesture monitor that could handle this event + std::vector gestureMonitors = + findTouchedGestureMonitorsLocked(displayId, {}); + for (TouchedMonitor& gestureMonitor : gestureMonitors) { + sp connection = + getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); + if (connection->responsive) { + // This monitor could take more input. Drop all events preceding this + // event, so that gesture monitor could get a chance to receive the stream + ALOGW("Pruning the input queue because %s is unresponsive, but we have a " + "responsive gesture monitor that may handle the event", + mAwaitedFocusedApplication->getName().c_str()); + return true; + } + } + } + + // Prevent getting stuck: if we have a pending key event, and some motion events that have not + // yet been processed by some connections, the dispatcher will wait for these motion + // events to be processed before dispatching the key event. This is because these motion events + // may cause a new window to be launched, which the user might expect to receive focus. + // To prevent waiting forever for such events, just send the key to the currently focused window + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the focused window."); + mKeyIsWaitingForEventsTimeout = now(); } return false; } @@ -694,10 +775,6 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { } case EventEntry::Type::MOTION: { - // Optimize case where the current application is unresponsive and the user - // decides to touch a window in a different application. - // If the application takes too long to catch up then we drop all events preceding - // the touch into the other window. if (shouldPruneInboundQueueLocked(static_cast(*entry))) { mNextUnblockedEvent = entry; needWake = true; @@ -912,7 +989,6 @@ void InputDispatcher::drainInboundQueueLocked() { void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { - resetAnrTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); mPendingEvent = nullptr; } @@ -1294,109 +1370,29 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event } } -int32_t InputDispatcher::handleTargetsNotReadyLocked( - nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { - if (applicationHandle == nullptr && windowHandle == nullptr) { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); - } - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = LONG_LONG_MAX; - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - } - } else { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - ALOGI("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - std::chrono::nanoseconds timeout; - if (windowHandle != nullptr) { - timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else if (applicationHandle != nullptr) { - timeout = - applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else { - timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; - } - - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout.count(); - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - - if (windowHandle != nullptr) { - mInputTargetWaitApplicationToken = windowHandle->getApplicationToken(); - } - if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) { - mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken(); - } - } - } - - if (mInputTargetWaitTimeoutExpired) { - return INPUT_EVENT_INJECTION_TIMED_OUT; - } - - if (currentTime >= mInputTargetWaitTimeoutTime) { - onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, - mInputTargetWaitStartTime, reason); - - // Force poll loop to wake up immediately on next iteration once we get the - // ANR response back from the policy. - *nextWakeupTime = LONG_LONG_MIN; - return INPUT_EVENT_INJECTION_PENDING; - } else { - // Force poll loop to wake up when timeout is due. - if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = mInputTargetWaitTimeoutTime; - } - return INPUT_EVENT_INJECTION_PENDING; - } -} - -void InputDispatcher::removeWindowByTokenLocked(const sp& token) { - for (std::pair& pair : mTouchStatesByDisplay) { - TouchState& state = pair.second; - state.removeWindowByToken(token); - } -} - -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t timeoutExtension, const sp& inputConnectionToken) { - if (timeoutExtension > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + timeoutExtension; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Input state will not be realistic. Mark it out of sync. - sp connection = getConnectionLocked(inputConnectionToken); - if (connection != nullptr) { - removeWindowByTokenLocked(inputConnectionToken); - - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } - } +void InputDispatcher::cancelEventsForAnrLocked(const sp& connection) { + // We will not be breaking any connections here, even if the policy wants us to abort dispatch. + // If the policy decides to close the app, we will get a channel removal event via + // unregisterInputChannel, and will clean up the connection that way. We are already not + // sending new pointers to the connection when it blocked, but focused events will continue to + // pile up. + ALOGW("Canceling events for %s because it is unresponsive", + connection->inputChannel->getName().c_str()); + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } -void InputDispatcher::resetAnrTimeoutsLocked() { +void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { if (DEBUG_FOCUS) { ALOGD("Resetting ANR timeouts."); } // Reset input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplicationToken.clear(); + mNoFocusedWindowTimeoutTime = std::nullopt; + mAwaitedFocusedApplication.clear(); } /** @@ -1427,6 +1423,36 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; } +bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, + const char* focusedWindowName) { + if (mAnrTracker.empty()) { + // already processed all events that we waited for + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; + } + + if (!mKeyIsWaitingForEventsTimeout.has_value()) { + // Start the timer + ALOGD("Waiting to send key to %s because there are unprocessed events that may cause " + "focus to change", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count(); + return true; + } + + // We still have pending events, and already started the timer + if (currentTime < *mKeyIsWaitingForEventsTimeout) { + return true; // Still waiting + } + + // Waited too long, and some connection still hasn't processed all motions + // Just send the key to the focused window + ALOGW("Dispatching key to %s even though there are other unprocessed events", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; +} + int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector& inputTargets, @@ -1441,31 +1467,70 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // If there is no currently focused window and no focused application // then drop the event. - if (focusedWindowHandle == nullptr) { - if (focusedApplicationHandle != nullptr) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - nullptr, nextWakeupTime, - "Waiting because no window has focus but there is " - "a focused application that may eventually add a " - "window when it finishes starting up."); - } - - ALOGI("Dropping event because there is no focused window or focused application in display " - "%" PRId32 ".", - displayId); + if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { + ALOGI("Dropping %s event because there is no focused window or focused application in " + "display %" PRId32 ".", + EventEntry::typeToString(entry.type), displayId); return INPUT_EVENT_INJECTION_FAILED; } + // Compatibility behavior: raise ANR if there is a focused application, but no focused window. + // Only start counting when we have a focused event to dispatch. The ANR is canceled if we + // start interacting with another application via touch (app switch). This code can be removed + // if the "no focused window ANR" is moved to the policy. Input doesn't know whether + // an app is expected to have a focused window. + if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) { + if (!mNoFocusedWindowTimeoutTime.has_value()) { + // We just discovered that there's no focused window. Start the ANR timer + const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT.count()); + mNoFocusedWindowTimeoutTime = currentTime + timeout; + mAwaitedFocusedApplication = focusedApplicationHandle; + ALOGW("Waiting because no window has focus but %s may eventually add a " + "window when it finishes starting up. Will wait for %" PRId64 "ms", + mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout)); + *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + return INPUT_EVENT_INJECTION_PENDING; + } else if (currentTime > *mNoFocusedWindowTimeoutTime) { + // Already raised ANR. Drop the event + ALOGE("Dropping %s event because there is no focused window", + EventEntry::typeToString(entry.type)); + return INPUT_EVENT_INJECTION_FAILED; + } else { + // Still waiting for the focused window + return INPUT_EVENT_INJECTION_PENDING; + } + } + + // we have a valid, non-null focused window + resetNoFocusedWindowTimeoutLocked(); + // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { return INPUT_EVENT_INJECTION_PERMISSION_DENIED; } - // Check whether the window is ready for more input. - reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - focusedWindowHandle, nextWakeupTime, reason.c_str()); + if (focusedWindowHandle->getInfo()->paused) { + ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); + return INPUT_EVENT_INJECTION_PENDING; + } + + // If the event is a key event, then we must wait for all previous events to + // complete before delivering it because previous events may have the + // side-effect of transferring focus to a different window and we want to + // ensure that the following keys are sent to the new window. + // + // Suppose the user touches a button in a window then immediately presses "A". + // If the button causes a pop-up window to appear then we want to ensure that + // the "A" key is delivered to the new pop-up window. This is because users + // often anticipate pending UI changes when typing on a keyboard. + // To obtain this behavior, we must serialize key events with respect to all + // prior input events. + if (entry.type == EventEntry::Type::KEY) { + if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { + *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + return INPUT_EVENT_INJECTION_PENDING; + } } // Success! Output targets. @@ -1477,6 +1542,32 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, return INPUT_EVENT_INJECTION_SUCCEEDED; } +/** + * Given a list of monitors, remove the ones we cannot find a connection for, and the ones + * that are currently unresponsive. + */ +std::vector InputDispatcher::selectResponsiveMonitorsLocked( + const std::vector& monitors) const { + std::vector responsiveMonitors; + std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), + [this](const TouchedMonitor& monitor) REQUIRES(mLock) { + sp connection = getConnectionLocked( + monitor.monitor.inputChannel->getConnectionToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for monitor %s", + monitor.monitor.inputChannel->getName().c_str()); + return false; + } + if (!connection->responsive) { + ALOGW("Unresponsive monitor %s will not get the new gesture", + connection->inputChannel->getName().c_str()); + return false; + } + return true; + }); + return responsiveMonitors; +} + int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, std::vector& inputTargets, @@ -1592,6 +1683,29 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } + if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) { + ALOGI("Not sending touch event to %s because it is paused", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } + + if (newTouchedWindowHandle != nullptr) { + sp connection = getConnectionLocked(newTouchedWindowHandle->getToken()); + if (connection == nullptr) { + ALOGI("Could not find connection for %s", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } else if (!connection->responsive) { + // don't send the new touch to an unresponsive window + ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64, + newTouchedWindowHandle->getName().c_str(), entry.eventTime); + newTouchedWindowHandle = nullptr; + } + } + + // Also don't send the new touch event to unresponsive gesture monitors + newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); + if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " "(%d, %d) in display %" PRId32 ".", @@ -1758,21 +1872,6 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } - // Ensure all touched foreground windows are ready for new input. - for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // Check whether the window is ready for more input. - std::string reason = - checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, - entry, "touched"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, nullptr, - touchedWindow.windowHandle, nextWakeupTime, - reason.c_str()); - } - } - } - // If this is the first pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. @@ -2050,92 +2149,6 @@ bool InputDispatcher::isWindowObscuredLocked(const sp& window return false; } -std::string InputDispatcher::checkWindowReadyForMoreInputLocked( - nsecs_t currentTime, const sp& windowHandle, - const EventEntry& eventEntry, const char* targetType) { - // If the window is paused then keep waiting. - if (windowHandle->getInfo()->paused) { - return StringPrintf("Waiting because the %s window is paused.", targetType); - } - - // If the window's connection is not registered then keep waiting. - sp connection = getConnectionLocked(windowHandle->getToken()); - if (connection == nullptr) { - return StringPrintf("Waiting because the %s window's input channel is not " - "registered with the input dispatcher. The window may be in the " - "process of being removed.", - targetType); - } - - // If the connection is dead then keep waiting. - if (connection->status != Connection::STATUS_NORMAL) { - return StringPrintf("Waiting because the %s window's input connection is %s." - "The window may be in the process of being removed.", - targetType, connection->getStatusLabel()); - } - - // If the connection is backed up then keep waiting. - if (connection->inputPublisherBlocked) { - return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %zu. Wait queue length: %zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - - // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry.type == EventEntry::Type::KEY) { - // If the event is a key event, then we must wait for all previous events to - // complete before delivering it because previous events may have the - // side-effect of transferring focus to a different window and we want to - // ensure that the following keys are sent to the new window. - // - // Suppose the user touches a button in a window then immediately presses "A". - // If the button causes a pop-up window to appear then we want to ensure that - // the "A" key is delivered to the new pop-up window. This is because users - // often anticipate pending UI changes when typing on a keyboard. - // To obtain this behavior, we must serialize key events with respect to all - // prior input events. - if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { - return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %zu. Wait queue length: " - "%zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - } else { - // Touch events can always be sent to a window immediately because the user intended - // to touch whatever was visible at the time. Even if focus changes or a new - // window appears moments later, the touch event was meant to be delivered to - // whatever window happened to be on screen at the time. - // - // Generic motion events, such as trackball or joystick events are a little trickier. - // Like key events, generic motion events are delivered to the focused window. - // Unlike key events, generic motion events don't tend to transfer focus to other - // windows and it is not important for them to be serialized. So we prefer to deliver - // generic motion events as soon as possible to improve efficiency and reduce lag - // through batching. - // - // The one case where we pause input event delivery is when the wait queue is piling - // up with lots of events because the application is not responding. - // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.empty() && - currentTime >= - connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { - return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to " - "it over " - "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " - "%0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.size(), - (currentTime - connection->waitQueue.front()->deliveryTime) * - 0.000001f); - } - } - return ""; -} - std::string InputDispatcher::getApplicationWindowLabel( const sp& applicationHandle, const sp& windowHandle) { @@ -2535,6 +2548,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; + const nsecs_t timeout = + getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); + dispatchEntry->timeoutTime = currentTime + timeout; // Publish the event. status_t status; @@ -2654,7 +2670,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, "waiting for the application to catch up", connection->getInputChannelName().c_str()); #endif - connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " @@ -2671,6 +2686,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry)); traceOutboundQueueLength(connection); connection->waitQueue.push_back(dispatchEntry); + if (connection->responsive) { + mAnrTracker.insert(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + } traceWaitQueueLength(connection); } } @@ -2705,8 +2724,6 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), seq, toString(handled)); #endif - connection->inputPublisherBlocked = false; - if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { return; @@ -3862,15 +3879,17 @@ void InputDispatcher::setFocusedApplication( sp oldFocusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + + if (oldFocusedApplicationHandle == mAwaitedFocusedApplication && + inputApplicationHandle != oldFocusedApplicationHandle) { + resetNoFocusedWindowTimeoutLocked(); + } + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { if (oldFocusedApplicationHandle != inputApplicationHandle) { - if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); - } mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } } else if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } @@ -3951,7 +3970,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); } if (mDispatchEnabled && !enabled) { @@ -4091,8 +4110,9 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); + mAnrTracker.clear(); mTouchStatesByDisplay.clear(); mLastHoverWindowHandle.clear(); mReplacedKeys.clear(); @@ -4289,11 +4309,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (const auto& pair : mConnectionsByFd) { const sp& connection = pair.second; dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + "status=%s, monitor=%s, responsive=%s\n", pair.first, connection->getInputChannelName().c_str(), connection->getWindowName().c_str(), connection->getStatusLabel(), - toString(connection->monitor), - toString(connection->inputPublisherBlocked)); + toString(connection->monitor), toString(connection->responsive)); if (!connection->outboundQueue.empty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", @@ -4561,6 +4580,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn } void InputDispatcher::removeConnectionLocked(const sp& connection) { + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); removeByValue(mConnectionsByFd, connection); } @@ -4598,17 +4618,69 @@ void InputDispatcher::onFocusChangedLocked(const sp& oldFocus postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onAnrLocked(nsecs_t currentTime, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) { - float dispatchLatency = (currentTime - eventTime) * 0.000001f; - float waitDuration = (currentTime - waitStartTime) * 0.000001f; - ALOGI("Application is not responding: %s. " - "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, - waitDuration, reason); +void InputDispatcher::onAnrLocked(const sp& connection) { + // Since we are allowing the policy to extend the timeout, maybe the waitQueue + // is already healthy again. Don't raise ANR in this situation + if (connection->waitQueue.empty()) { + ALOGI("Not raising ANR because the connection %s has recovered", + connection->inputChannel->getName().c_str()); + return; + } + /** + * The "oldestEntry" is the entry that was first sent to the application. That entry, however, + * may not be the one that caused the timeout to occur. One possibility is that window timeout + * has changed. This could cause newer entries to time out before the already dispatched + * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app + * processes the events linearly. So providing information about the oldest entry seems to be + * most useful. + */ + DispatchEntry* oldestEntry = *connection->waitQueue.begin(); + const nsecs_t currentWait = now() - oldestEntry->deliveryTime; + std::string reason = + android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", + connection->inputChannel->getName().c_str(), + ns2ms(currentWait), + oldestEntry->eventEntry->getDescription().c_str()); + + updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()), + reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = nullptr; + commandEntry->inputChannel = connection->inputChannel; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} +void InputDispatcher::onAnrLocked(const sp& application) { + std::string reason = android::base::StringPrintf("%s does not have a focused window", + application->getName().c_str()); + + updateLastAnrStateLocked(application, reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = application; + commandEntry->inputChannel = nullptr; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& window, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(nullptr, window); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& application, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(application, nullptr); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel, + const std::string& reason) { // Capture a record of the InputDispatcher state at the time of the ANR. time_t t = time(nullptr); struct tm tm; @@ -4618,21 +4690,9 @@ void InputDispatcher::onAnrLocked(nsecs_t currentTime, mLastAnrState.clear(); mLastAnrState += INDENT "ANR:\n"; mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastAnrState += - StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); - mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); - mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); - mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason); + mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str()); + mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str()); dumpDispatchStateLocked(mLastAnrState); - - std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); - commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputChannel = - windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; - commandEntry->reason = reason; - postCommandLocked(std::move(commandEntry)); } void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { @@ -4673,13 +4733,50 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); + if (timeoutExtension > 0) { + extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension); + } else { + // stop waking up for events in this connection, it is already not responding + sp connection = getConnectionLocked(token); + if (connection == nullptr) { + return; + } + cancelEventsForAnrLocked(connection); + } +} + +void InputDispatcher::extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, + nsecs_t timeoutExtension) { + sp connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) { + // Maybe ANR happened because there's no focused window? + mNoFocusedWindowTimeoutTime = now() + timeoutExtension; + mAwaitedFocusedApplication = application; + } else { + // It's also possible that the connection already disappeared. No action necessary. + } + return; + } + + ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer", + connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension)); + + connection->responsive = true; + const nsecs_t newTimeout = now() + timeoutExtension; + for (DispatchEntry* entry : connection->waitQueue) { + if (newTimeout >= entry->timeoutTime) { + // Already removed old entries when connection was marked unresponsive + entry->timeoutTime = newTimeout; + mAnrTracker.insert(entry->timeoutTime, connectionToken); + } + } } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; - KeyEvent event = createKeyEvent(*entry); mLock.unlock(); @@ -4713,6 +4810,20 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr mLock.lock(); } +/** + * Connection is responsive if it has no events in the waitQueue that are older than the + * current time. + */ +static bool isConnectionResponsive(const Connection& connection) { + const nsecs_t currentTime = now(); + for (const DispatchEntry* entry : connection.waitQueue) { + if (entry->timeoutTime < currentTime) { + return false; + } + } + return true; +} + void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { sp connection = commandEntry->connection; const nsecs_t finishTime = commandEntry->eventTime; @@ -4725,7 +4836,6 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c return; } DispatchEntry* dispatchEntry = *dispatchEntryIt; - const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), @@ -4754,6 +4864,11 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c if (dispatchEntryIt != connection->waitQueue.end()) { dispatchEntry = *dispatchEntryIt; connection->waitQueue.erase(dispatchEntryIt); + mAnrTracker.erase(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + if (!connection->responsive) { + connection->responsive = isConnectionResponsive(*connection); + } traceWaitQueueLength(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.push_front(dispatchEntry); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1980435b5a..7c2028ab29 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H +#include "AnrTracker.h" #include "CancelationOptions.h" #include "Entry.h" #include "InjectionState.h" @@ -216,7 +217,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -274,6 +274,9 @@ private: bool runCommandsLockedInterruptible() REQUIRES(mLock); void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); + nsecs_t processAnrsLocked() REQUIRES(mLock); + nsecs_t getDispatchingTimeoutLocked(const sp& token) REQUIRES(mLock); + // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); @@ -344,38 +347,53 @@ private: void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); - // Keeping track of ANR timeouts. - enum InputTargetWaitCause { - INPUT_TARGET_WAIT_CAUSE_NONE, - INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, - INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, - }; - - InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); - nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); - nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); - bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); - sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); + /** + * This field is set if there is no focused window, and we have an event that requires + * a focused window to be dispatched (for example, a KeyEvent). + * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before + * dropping the event and raising an ANR for that application. + * This is useful if an application is slow to add a focused window. + */ + std::optional mNoFocusedWindowTimeoutTime GUARDED_BY(mLock); bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); - // Contains the last window which received a hover event. - sp mLastHoverWindowHandle GUARDED_BY(mLock); + /** + * Time to stop waiting for the events to be processed while trying to dispatch a key. + * When this time expires, we just send the pending key event to the currently focused window, + * without waiting on other events to be processed first. + */ + std::optional mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock); + bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName) + REQUIRES(mLock); - // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) + /** + * The focused application at the time when no focused window was present. + * Used to raise an ANR when we have no focused window. + */ + sp mAwaitedFocusedApplication GUARDED_BY(mLock); + + // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next. + // AnrTracker must be kept in-sync with all responsive connection.waitQueues. + // If a connection is not responsive, then the entries should not be added to the AnrTracker. + // Once a connection becomes unresponsive, its entries are removed from AnrTracker to + // prevent unneeded wakeups. + AnrTracker mAnrTracker GUARDED_BY(mLock); + void extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, nsecs_t timeoutExtension) REQUIRES(mLock); - void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); + // Contains the last window which received a hover event. + sp mLastHoverWindowHandle GUARDED_BY(mLock); - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputConnectionToken) - REQUIRES(mLock); + void cancelEventsForAnrLocked(const sp& connection) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetAnrTimeoutsLocked() REQUIRES(mLock); + // If a focused application changes, we should stop counting down the "no focused window" time, + // because we will have no way of knowing when the previous application actually added a window. + // This also means that we will miss cases like pulling down notification shade when the + // focused application does not have a focused window (no ANR will be raised if notification + // shade is pulled down while we are counting down the timeout). + void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry& entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, @@ -388,6 +406,8 @@ private: std::vector findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector>& portalWindows) const REQUIRES(mLock); + std::vector selectResponsiveMonitorsLocked( + const std::vector& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) @@ -406,11 +426,6 @@ private: std::string getApplicationWindowLabel(const sp& applicationHandle, const sp& windowHandle); - std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp& windowHandle, - const EventEntry& eventEntry, - const char* targetType) REQUIRES(mLock); - // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. @@ -480,9 +495,14 @@ private: REQUIRES(mLock); void onFocusChangedLocked(const sp& oldFocus, const sp& newFocus) REQUIRES(mLock); - void onAnrLocked(nsecs_t currentTime, const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); + void onAnrLocked(const sp& connection) REQUIRES(mLock); + void onAnrLocked(const sp& application) REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& window, const std::string& reason) + REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& application, + const std::string& reason) REQUIRES(mLock); + void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) + REQUIRES(mLock); // Outbound policy interactions. void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 73d22727f0..a0d2f4f172 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -27,6 +27,7 @@ cc_test { "libinputflinger_defaults", ], srcs: [ + "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "EventHub_test.cpp", "TestInputListener.cpp", diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp new file mode 100644 index 0000000000..b561da107d --- /dev/null +++ b/services/inputflinger/tests/AnrTracker_test.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../AnrTracker.h" + +#include +#include + +namespace android { + +namespace inputdispatcher { + +// --- AnrTrackerTest --- + +/** + * Add a single entry and ensure it's returned as first, even if the token isn't valid + */ +TEST(AnrTrackerTest, SingleEntry_First) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(tracker.firstToken(), nullptr); +} + +TEST(AnrTrackerTest, MultipleEntries_RemoveToken) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(1, token1); + tracker.insert(2, token2); + tracker.insert(3, token1); + tracker.insert(4, token2); + tracker.insert(5, token1); + + tracker.eraseToken(token1); + + ASSERT_EQ(2, tracker.firstTimeout()); +} + +TEST(AnrTrackerTest, AddAndRemove_Empty) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(1, nullptr); + ASSERT_FALSE(tracker.empty()); + + tracker.erase(1, nullptr); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, Clear) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + tracker.clear(); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, SingleToken_MaintainsOrder) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(2, nullptr); + tracker.insert(5, nullptr); + tracker.insert(0, nullptr); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(5, token2); + tracker.insert(0, token2); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(token2, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + // Doesn't matter which token is returned - both are valid results + ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + tracker.erase(2, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + ASSERT_EQ(token1, tracker.firstToken()); +} + +TEST(AnrTrackerTest, Empty_DoesntCrash) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout()); + // Can't call firstToken() if tracker.empty() +} + +TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + // Remove with non-matching timestamp + tracker.erase(2, nullptr); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with non-matching token + tracker.erase(1, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with both non-matching + tracker.erase(2, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +} // namespace inputdispatcher + +} // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 13e835427f..1a133dc01c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -126,6 +126,14 @@ public: void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, const sp& expectedApplication, const sp& expectedToken) { + std::pair, sp> anrData; + ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout)); + ASSERT_EQ(expectedApplication, anrData.first); + ASSERT_EQ(expectedToken, anrData.second); + } + + std::pair, sp> getNotifyAnrData( + std::chrono::nanoseconds timeout) { const std::chrono::time_point start = std::chrono::steady_clock::now(); std::unique_lock lock(mLock); std::chrono::duration timeToWait = timeout + 100ms; // provide some slack @@ -136,16 +144,33 @@ public: // before checking if ANR was called. // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide // it some time to act. 100ms seems reasonable. - mNotifyAnr.wait_for(lock, timeToWait, - [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) { + return !mAnrApplications.empty() && !mAnrWindowTokens.empty(); + }); const std::chrono::duration waited = std::chrono::steady_clock::now() - start; - ASSERT_TRUE(mNotifyAnrWasCalled); + if (mAnrApplications.empty() || mAnrWindowTokens.empty()) { + ADD_FAILURE() << "Did not receive ANR callback"; + } // Ensure that the ANR didn't get raised too early. We can't be too strict here because // the dispatcher started counting before this function was called - ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for - mNotifyAnrWasCalled = false; - ASSERT_EQ(expectedApplication, mLastAnrApplication); - ASSERT_EQ(expectedToken, mLastAnrWindowToken); + if (std::chrono::abs(timeout - waited) > 100ms) { + ADD_FAILURE() << "ANR was raised too early or too late. Expected " + << std::chrono::duration_cast(timeout).count() + << "ms, but waited " + << std::chrono::duration_cast(waited).count() + << "ms instead"; + } + std::pair, sp> result = + std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front()); + mAnrApplications.pop(); + mAnrWindowTokens.pop(); + return result; + } + + void assertNotifyAnrWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mAnrApplications.empty()); + ASSERT_TRUE(mAnrWindowTokens.empty()); } void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { @@ -153,6 +178,8 @@ public: mConfig.keyRepeatDelay = delay; } + void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; } + private: std::mutex mLock; std::unique_ptr mFilteredEvent GUARDED_BY(mLock); @@ -161,9 +188,8 @@ private: std::optional mLastNotifySwitch GUARDED_BY(mLock); // ANR handling - bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; - sp mLastAnrApplication GUARDED_BY(mLock); - sp mLastAnrWindowToken GUARDED_BY(mLock); + std::queue> mAnrApplications GUARDED_BY(mLock); + std::queue> mAnrWindowTokens GUARDED_BY(mLock); std::condition_variable mNotifyAnr; std::chrono::nanoseconds mAnrTimeout = 0ms; @@ -175,9 +201,8 @@ private: virtual nsecs_t notifyAnr(const sp& application, const sp& windowToken, const std::string&) override { std::scoped_lock lock(mLock); - mLastAnrApplication = application; - mLastAnrWindowToken = windowToken; - mNotifyAnrWasCalled = true; + mAnrApplications.push(application); + mAnrWindowTokens.push(windowToken); mNotifyAnr.notify_all(); return mAnrTimeout.count(); } @@ -643,7 +668,7 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType) << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); @@ -688,9 +713,24 @@ public: void assertNoEvents() { InputEvent* event = consume(); - ASSERT_EQ(nullptr, event) - << mName.c_str() - << ": should not have received any events, so consume() should return NULL"; + if (event == nullptr) { + return; + } + if (event->getType() == AINPUT_EVENT_TYPE_KEY) { + KeyEvent& keyEvent = static_cast(*event); + ADD_FAILURE() << "Received key event " + << KeyEvent::actionToString(keyEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) { + MotionEvent& motionEvent = static_cast(*event); + ADD_FAILURE() << "Received motion event " + << MotionEvent::actionToString(motionEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) { + FocusEvent& focusEvent = static_cast(*event); + ADD_FAILURE() << "Received focus event, hasFocus = " + << (focusEvent.getHasFocus() ? "true" : "false"); + } + FAIL() << mName.c_str() + << ": should not have received any events, so consume() should return NULL"; } sp getToken() { return mConsumer->getChannel()->getConnectionToken(); } @@ -754,6 +794,8 @@ public: mInfo.dispatchingTimeout = timeout.count(); } + void setPaused(bool paused) { mInfo.paused = paused; } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -775,6 +817,10 @@ public: expectedFlags); } + void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); + } + void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId, @@ -826,12 +872,12 @@ public: expectedFlags); } - std::optional receiveEvent() { + std::optional receiveEvent(InputEvent** outEvent = nullptr) { if (mInputReceiver == nullptr) { ADD_FAILURE() << "Invalid receive event on window with no receiver"; return std::nullopt; } - return mInputReceiver->receiveEvent(); + return mInputReceiver->receiveEvent(outEvent); } void finishEvent(uint32_t sequenceNum) { @@ -865,7 +911,9 @@ private: std::atomic FakeWindowHandle::sId{1}; static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, - int32_t displayId = ADISPLAY_ID_NONE) { + int32_t displayId = ADISPLAY_ID_NONE, + int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -875,10 +923,9 @@ static int32_t injectKey(const sp& dispatcher, int32_t action, repeatCount, currentTime, currentTime); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectKeyDown(const sp& dispatcher, @@ -886,11 +933,19 @@ static int32_t injectKeyDown(const sp& dispatcher, return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } +static int32_t injectKeyUp(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); +} + static int32_t injectMotionEvent( const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, const PointF& position, const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION}) { + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, + int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -903,7 +958,6 @@ static int32_t injectMotionEvent( pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, @@ -911,14 +965,13 @@ static int32_t injectMotionEvent( /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, - currentTime, currentTime, + eventTime, eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectMotionDown(const sp& dispatcher, int32_t source, @@ -1429,6 +1482,10 @@ public: expectedDisplayId, expectedFlags); } + std::optional receiveEvent() { return mInputReceiver->receiveEvent(); } + + void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); } + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -1507,6 +1564,21 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); } +TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); + std::optional consumeSeq = monitor.receiveEvent(); + ASSERT_TRUE(consumeSeq); + + mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken()); + monitor.finishEvent(*consumeSeq); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { sp application = new FakeApplicationHandle(); sp window = @@ -2329,23 +2401,40 @@ protected: } }; +// Send a tap and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) { + tapOnWindow(); + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Send a regular key and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + // Send an event to the app and have the app not respond right away. -// Make sure that ANR is raised +// When ANR is raised, policy will tell the dispatcher to cancel the events for that window. +// So InputDispatcher will enqueue ACTION_CANCEL event as well. TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); - // Also, overwhelm the socket to make sure ANR starts - for (size_t i = 0; i < 100; i++) { - injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); - } - std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + + // The remaining lines are not really needed for the test, but kept as a sanity check + mWindow->finishEvent(*sequenceNum); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); ASSERT_TRUE(mDispatcher->waitForIdle()); } @@ -2355,14 +2444,591 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); std::optional sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // taps on the window work as normal + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown()); + mDispatcher->waitForIdle(); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +// If the policy wants to keep waiting on the focused window to be added, make sure +// that this timeout extension is honored and ANR is raised again. +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration appTimeout = + mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/); + + // After the extended time has passed, ANR should be raised again + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + + // If we stop extending the timeout, dispatcher should go to idle. + // Another ANR may be raised during this time + mFakePolicy->setAnrTimeout(0ms); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // Once a focused event arrives, we get an ANR for this application + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); - // Start ANR process by sending a 2nd key, which would trigger the check for whether - // waitQueue is empty - injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + // Future focused events get dropped right away + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mWindow->assertNoEvents(); +} + +/** + * Ensure that the implementation is valid. Since we are using multiset to keep track of the + * ANR timeouts, we are allowing entries with identical timestamps in the same connection. + * If we process 1 of the events, but ANR on the second event with the same timestamp, + * the ANR mechanism should still work. + * + * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the + * DOWN event, while not responding on the second one. + */ +TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) { + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // Now send ACTION_UP, with identical timestamp + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // We have now sent down and up. Let's consume first event and then ANR on the second. + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); +} + +// If an app is not responding to a key event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT)); + + // Stuck on the ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If an app is not responding to a motion event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionDown(); + // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If a window is unresponsive, then you get anr. if the window later catches up and starts to +// process events, you don't get an anr. When the window later becomes unresponsive again, you +// get an ANR again. +// 1. tap -> block on ACTION_UP -> receive ANR +// 2. consume all pending events (= queue becomes healthy again) +// 3. tap again -> block on ACTION_UP again -> receive ANR second time +TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) { + tapOnWindow(); + + mWindow->consumeMotionDown(); + // Block on ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); // Now the connection should be healthy again + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + + tapOnWindow(); + mWindow->consumeMotionDown(); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); + + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); +} + +// If the policy tells us to raise ANR again after some time, ensure that the timeout extension +// is honored +TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) { + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/, + mWindow->getToken()); + + // Since the policy wanted to extend ANR, make sure it is called again after the extension + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mFakePolicy->setAnrTimeout(0ms); + std::this_thread::sleep_for(windowTimeout); + // We are not checking if ANR has been called, because it may have been called again by the + // time we set the timeout to 0 + + // When the policy finally says stop, we should get ACTION_CANCEL + mWindow->consumeMotionDown(); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + mWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will "succeed" because we will eventually give up and send the key to the focused + // window even if motions are still being processed. But because the injection timeout is short, + // we will receive INJECTION_TIMED_OUT as the result. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + std::this_thread::sleep_for(500ms); + // if we wait long enough though, dispatcher will give up, and still send the key + // to the focused window, even though we have not yet finished the motion event + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mWindow->finishEvent(*downSequenceNum); + mWindow->finishEvent(*upSequenceNum); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not go to the focused window until the motion is processed. + * If then a new motion comes in, then the pending key event should be going to the currently + * focused window right away. + */ +TEST_F(InputDispatcherSingleWindowAnr, + PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection is async, so it will succeed + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, + ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE)); + // At this point, key is still pending, and should not be sent to the application yet. + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Now tap down again. It should cause the pending key to go to the focused window right away. + tapOnWindow(); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd + // the other events yet. We can finish events in any order. + mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN + mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + mWindow->assertNoEvents(); +} + +class InputDispatcherMultiWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(10ms); + mUnfocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT); + mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped + mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + mFocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); + mFocusedWindow->setDispatchingTimeout(10ms); + mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); + mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + mFocusedWindow->setFocus(true); + + // Expect one focus window exist in display. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + mFocusedWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + + mUnfocusedWindow.clear(); + mFocusedWindow.clear(); + } + +protected: + sp mApplication; + sp mUnfocusedWindow; + sp mFocusedWindow; + static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20}; + static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75}; + static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40}; + + void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); } + + void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); } + +private: + void tap(const PointF& location) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + } +}; + +// If we have 2 windows that are both unresponsive, the one with the shortest timeout +// should be ANR'd first. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeMotionDown(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // We consumed all events, so no ANR + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + std::optional unfocusedSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(unfocusedSequenceNum); + std::optional focusedSequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_TRUE(focusedSequenceNum); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mFocusedWindow->finishEvent(*focusedSequenceNum); + mUnfocusedWindow->finishEvent(*unfocusedSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// If we have 2 windows with identical timeouts that are both unresponsive, +// it doesn't matter which order they should have ANR. +// But we should receive ANR for both. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) { + // Set the timeout for unfocused window to match the focused window + mUnfocusedWindow->setDispatchingTimeout(10ms); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + tapOnFocusedWindow(); + // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window + std::pair, sp> anrData1 = + mFakePolicy->getNotifyAnrData(10ms); + std::pair, sp> anrData2 = + mFakePolicy->getNotifyAnrData(0ms); + + // We don't know which window will ANR first. But both of them should happen eventually. + ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second || + mFocusedWindow->getToken() == anrData2.second); + ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second || + mUnfocusedWindow->getToken() == anrData2.second); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If a window is already not responding, the second tap on the same window should be ignored. +// We should also log an error to account for the dropped event (not tested here). +// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events. +TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { + tapOnFocusedWindow(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // Receive the events, but don't respond + std::optional downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(downEventSequenceNum); + std::optional upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP + ASSERT_TRUE(upEventSequenceNum); + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + // Tap once again + // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a + // valid touch target + mUnfocusedWindow->assertNoEvents(); + + // Consume the first tap + mFocusedWindow->finishEvent(*downEventSequenceNum); + mFocusedWindow->finishEvent(*upEventSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // The second tap did not go to the focused window + mFocusedWindow->assertNoEvents(); + // should not have another ANR after the window just became healthy again + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If you tap outside of all windows, there will not be ANR +TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + LOCATION_OUTSIDE_ALL_WINDOWS)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Since the focused window is paused, tapping on it should not produce any events +TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) { + mFocusedWindow->setPaused(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + + std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // Should not ANR because the window is paused, and touches shouldn't go to it + mFakePolicy->assertNotifyAnrWasNotCalled(); + + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * If a different window becomes focused at this time, the key should go to that window instead. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) { + // Set a long ANR timeout to prevent it from triggering + mFocusedWindow->setDispatchingTimeout(2s); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + tapOnUnfocusedWindow(); + std::optional downSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will succeed because we will eventually give up and send the key to the focused + // window even if motions are still being processed. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there + mFocusedWindow->setFocus(false); + mUnfocusedWindow->setFocus(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + // Focus events should precede the key events + mUnfocusedWindow->consumeFocusEvent(true); + mFocusedWindow->consumeFocusEvent(false); + + // Finish the tap events, which should unblock dispatcher + mUnfocusedWindow->finishEvent(*downSequenceNum); + mUnfocusedWindow->finishEvent(*upSequenceNum); + + // Now that all queues are cleared and no backlog in the connections, the key event + // can finally go to the newly focused "mUnfocusedWindow". + mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +// When the touch stream is split across 2 windows, and one of them does not respond, +// then ANR should be raised and the touch should be canceled for the unresponsive window. +// The other window should not be affected by that. +TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { + // Touch Window 1 + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + motionArgs = + generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mUnfocusedWindow->consumeMotionDown(); + mFocusedWindow->consumeMotionDown(); + // Focused window may or may not receive ACTION_MOVE + // But it should definitely receive ACTION_CANCEL due to the ANR + InputEvent* event; + std::optional moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event); + ASSERT_TRUE(moveOrCancelSequenceNum); + mFocusedWindow->finishEvent(*moveOrCancelSequenceNum); + ASSERT_NE(nullptr, event); + ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION); + MotionEvent& motionEvent = static_cast(*event); + if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) { + mFocusedWindow->consumeMotionCancel(); + } else { + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction()); + } + ASSERT_TRUE(mDispatcher->waitForIdle()); + mUnfocusedWindow->assertNoEvents(); + mFocusedWindow->assertNoEvents(); } } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b From 7394eaa6ece3a05ada3b634d0b4bb63729611c3d Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 9 Apr 2020 11:16:18 -0700 Subject: Log windows that are being interacted with If the list of windows being interacted with changes, log the new list to event log. Example logs: 06-08 15:47:55.349 4105 4181 I input_interaction: Interaction with windows: ee638df NotificationShade (server), 06-08 15:48:03.917 4105 4181 I input_interaction: Interaction with windows: 399b259 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server), 06-08 15:48:10.844 4105 4181 I input_interaction: Interaction with windows: fcc1e60 com.android.systemui.ImageWallpaper (server), 399b259 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server), 06-08 15:49:30.091 4105 4181 I input_interaction: Interaction with windows: ee638df NotificationShade (server), 06-08 15:49:31.757 4105 4181 I input_interaction: Interaction with windows: fcc1e60 com.android.systemui.ImageWallpaper (server), 399b259 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity (server), 06-08 15:49:33.293 4105 4181 I input_interaction: Interaction with windows: Bug: 153092301 Test: adb logcat -b events | grep -i input Change-Id: Ia4216beb200163e60e05374b6ea363412bff55f3 Merged-In: Ia4216beb200163e60e05374b6ea363412bff55f3 --- services/inputflinger/dispatcher/Android.bp | 1 + .../inputflinger/dispatcher/EventLogTags.logtags | 42 ++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 78 +++++++++++++++++++++- services/inputflinger/dispatcher/InputDispatcher.h | 6 ++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 services/inputflinger/dispatcher/EventLogTags.logtags (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index a98f4b42d0..b242eec465 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -67,4 +67,5 @@ cc_library_static { export_header_lib_headers: [ "libinputdispatcher_headers", ], + logtags: ["EventLogTags.logtags"], } diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags new file mode 100644 index 0000000000..9c0f80ec22 --- /dev/null +++ b/services/inputflinger/dispatcher/EventLogTags.logtags @@ -0,0 +1,42 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# See system/core/logcat/event.logtags for the master copy of the tags. + +# 62000 - 62199 reserved for inputflinger + +62000 input_interaction (windows|4) +62001 input_focus (window|3) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform. \ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 46f6f44612..959eeea41e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -62,6 +62,7 @@ static constexpr bool DEBUG_FOCUS = false; #include #include #include +#include #include #include #include @@ -104,6 +105,10 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; +// Event log tags. See EventLogTags.logtags for reference +constexpr int LOGTAG_INPUT_INTERACTION = 62000; +constexpr int LOGTAG_INPUT_FOCUS = 62001; + static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -1028,7 +1033,9 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry target.inputChannel = channel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; entry->dispatchInProgress = true; - + std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + + channel->getName(); + android_log_event_list(LOGTAG_INPUT_FOCUS) << message << LOG_ID_EVENTS; dispatchEventLocked(currentTime, entry, {target}); } @@ -1266,6 +1273,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event ALOGD("dispatchEventToCurrentInputTargets"); #endif + updateInteractionTokensLocked(*eventEntry, inputTargets); + ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true pokeUserActivityLocked(*eventEntry); @@ -2417,6 +2426,73 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio traceOutboundQueueLength(connection); } +/** + * This function is purely for debugging. It helps us understand where the user interaction + * was taking place. For example, if user is touching launcher, we will see a log that user + * started interacting with launcher. In that example, the event would go to the wallpaper as well. + * We will see both launcher and wallpaper in that list. + * Once the interaction with a particular set of connections starts, no new logs will be printed + * until the set of interacted connections changes. + * + * The following items are skipped, to reduce the logspam: + * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged + * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions). + * This includes situations like the soft BACK button key. When the user releases (lifts up the + * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP. + * Both of those ACTION_UP events would not be logged + * Monitors (both gesture and global): any gesture monitors or global monitors receiving events + * will not be logged. This is omitted to reduce the amount of data printed. + * If you see , it's likely that one of the gesture monitors pilfered the event, and therefore + * gesture monitor is the only connection receiving the remainder of the gesture. + */ +void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, + const std::vector& targets) { + // Skip ACTION_UP events, and all events other than keys and motions + if (entry.type == EventEntry::Type::KEY) { + const KeyEntry& keyEntry = static_cast(entry); + if (keyEntry.action == AKEY_EVENT_ACTION_UP) { + return; + } + } else if (entry.type == EventEntry::Type::MOTION) { + const MotionEntry& motionEntry = static_cast(entry); + if (motionEntry.action == AMOTION_EVENT_ACTION_UP) { + return; + } + } else { + return; // Not a key or a motion + } + + std::unordered_set, IBinderHash> newConnections; + for (const InputTarget& target : targets) { + if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == + InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + continue; // Skip windows that receive ACTION_OUTSIDE + } + + sp token = target.inputChannel->getConnectionToken(); + sp connection = getConnectionLocked(token); // get connection + if (connection->monitor) { + continue; // We only need to keep track of the non-monitor connections. + } + + newConnections.insert(std::move(token)); + } + if (newConnections == mInteractionConnections) { + return; // no change + } + mInteractionConnections = newConnections; + std::string windowList; + for (const sp& token : newConnections) { + sp connection = getConnectionLocked(token); + windowList += connection->getWindowName() + ", "; + } + std::string message = "Interaction with windows: " + windowList; + if (windowList.empty()) { + message += ""; + } + android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS; +} + void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, const sp& newToken) { int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 0c3ea3674c..1980435b5a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -323,6 +324,11 @@ private: // Dispatcher state at time of last ANR. std::string mLastAnrState GUARDED_BY(mLock); + // The connection token of the channel that was last interacted with. + std::unordered_set, IBinderHash> mInteractionConnections GUARDED_BY(mLock); + void updateInteractionTokensLocked(const EventEntry& entry, + const std::vector& targets) REQUIRES(mLock); + // Dispatch inbound events. bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock); -- cgit v1.2.3-59-g8ed1b From 3d989c2a7188cda286356b7c7d4e6b9c514930ed Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 17 Jun 2020 21:42:12 +0000 Subject: Revert "Log windows that are being interacted with" This reverts commit 7394eaa6ece3a05ada3b634d0b4bb63729611c3d. Reason for revert: 159249116 Bug: 159249116 Change-Id: Ia91ee20b4828c141f7ff6b835341c132a3a18d79 --- services/inputflinger/dispatcher/Android.bp | 1 - .../inputflinger/dispatcher/EventLogTags.logtags | 42 ------------ .../inputflinger/dispatcher/InputDispatcher.cpp | 78 +--------------------- services/inputflinger/dispatcher/InputDispatcher.h | 6 -- 4 files changed, 1 insertion(+), 126 deletions(-) delete mode 100644 services/inputflinger/dispatcher/EventLogTags.logtags (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b242eec465..a98f4b42d0 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -67,5 +67,4 @@ cc_library_static { export_header_lib_headers: [ "libinputdispatcher_headers", ], - logtags: ["EventLogTags.logtags"], } diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags deleted file mode 100644 index 9c0f80ec22..0000000000 --- a/services/inputflinger/dispatcher/EventLogTags.logtags +++ /dev/null @@ -1,42 +0,0 @@ -# The entries in this file map a sparse set of log tag numbers to tag names. -# This is installed on the device, in /system/etc, and parsed by logcat. -# -# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the -# negative values alone for now.) -# -# Tag names are one or more ASCII letters and numbers or underscores, i.e. -# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former -# impacts log readability, the latter makes regex searches more annoying). -# -# Tag numbers and names are separated by whitespace. Blank lines and lines -# starting with '#' are ignored. -# -# Optionally, after the tag names can be put a description for the value(s) -# of the tag. Description are in the format -# (|data type[|data unit]) -# Multiple values are separated by commas. -# -# The data type is a number from the following values: -# 1: int -# 2: long -# 3: string -# 4: list -# -# The data unit is a number taken from the following list: -# 1: Number of objects -# 2: Number of bytes -# 3: Number of milliseconds -# 4: Number of allocations -# 5: Id -# 6: Percent -# Default value for data of type int/long is 2 (bytes). -# -# See system/core/logcat/event.logtags for the master copy of the tags. - -# 62000 - 62199 reserved for inputflinger - -62000 input_interaction (windows|4) -62001 input_focus (window|3) - -# NOTE - the range 1000000-2000000 is reserved for partners and others who -# want to define their own log tags without conflicting with the core platform. \ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 959eeea41e..46f6f44612 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -62,7 +62,6 @@ static constexpr bool DEBUG_FOCUS = false; #include #include #include -#include #include #include #include @@ -105,10 +104,6 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; -// Event log tags. See EventLogTags.logtags for reference -constexpr int LOGTAG_INPUT_INTERACTION = 62000; -constexpr int LOGTAG_INPUT_FOCUS = 62001; - static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -1033,9 +1028,7 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry target.inputChannel = channel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; entry->dispatchInProgress = true; - std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + - channel->getName(); - android_log_event_list(LOGTAG_INPUT_FOCUS) << message << LOG_ID_EVENTS; + dispatchEventLocked(currentTime, entry, {target}); } @@ -1273,8 +1266,6 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event ALOGD("dispatchEventToCurrentInputTargets"); #endif - updateInteractionTokensLocked(*eventEntry, inputTargets); - ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true pokeUserActivityLocked(*eventEntry); @@ -2426,73 +2417,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio traceOutboundQueueLength(connection); } -/** - * This function is purely for debugging. It helps us understand where the user interaction - * was taking place. For example, if user is touching launcher, we will see a log that user - * started interacting with launcher. In that example, the event would go to the wallpaper as well. - * We will see both launcher and wallpaper in that list. - * Once the interaction with a particular set of connections starts, no new logs will be printed - * until the set of interacted connections changes. - * - * The following items are skipped, to reduce the logspam: - * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged - * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions). - * This includes situations like the soft BACK button key. When the user releases (lifts up the - * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP. - * Both of those ACTION_UP events would not be logged - * Monitors (both gesture and global): any gesture monitors or global monitors receiving events - * will not be logged. This is omitted to reduce the amount of data printed. - * If you see , it's likely that one of the gesture monitors pilfered the event, and therefore - * gesture monitor is the only connection receiving the remainder of the gesture. - */ -void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, - const std::vector& targets) { - // Skip ACTION_UP events, and all events other than keys and motions - if (entry.type == EventEntry::Type::KEY) { - const KeyEntry& keyEntry = static_cast(entry); - if (keyEntry.action == AKEY_EVENT_ACTION_UP) { - return; - } - } else if (entry.type == EventEntry::Type::MOTION) { - const MotionEntry& motionEntry = static_cast(entry); - if (motionEntry.action == AMOTION_EVENT_ACTION_UP) { - return; - } - } else { - return; // Not a key or a motion - } - - std::unordered_set, IBinderHash> newConnections; - for (const InputTarget& target : targets) { - if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == - InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - continue; // Skip windows that receive ACTION_OUTSIDE - } - - sp token = target.inputChannel->getConnectionToken(); - sp connection = getConnectionLocked(token); // get connection - if (connection->monitor) { - continue; // We only need to keep track of the non-monitor connections. - } - - newConnections.insert(std::move(token)); - } - if (newConnections == mInteractionConnections) { - return; // no change - } - mInteractionConnections = newConnections; - std::string windowList; - for (const sp& token : newConnections) { - sp connection = getConnectionLocked(token); - windowList += connection->getWindowName() + ", "; - } - std::string message = "Interaction with windows: " + windowList; - if (windowList.empty()) { - message += ""; - } - android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS; -} - void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, const sp& newToken) { int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1980435b5a..0c3ea3674c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -48,7 +48,6 @@ #include #include #include -#include #include #include @@ -324,11 +323,6 @@ private: // Dispatcher state at time of last ANR. std::string mLastAnrState GUARDED_BY(mLock); - // The connection token of the channel that was last interacted with. - std::unordered_set, IBinderHash> mInteractionConnections GUARDED_BY(mLock); - void updateInteractionTokensLocked(const EventEntry& entry, - const std::vector& targets) REQUIRES(mLock); - // Dispatch inbound events. bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock); -- cgit v1.2.3-59-g8ed1b From 34ed4d4d74ce45e58e3934d472f28617d9b34296 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 18 Jun 2020 00:43:02 +0000 Subject: Ensure connection is non-null The function getConnectionLocked can return null. We must check the result for null to prevent dereferencing null. Bug: 143459140 Test: none Change-Id: I43991ea2ac9e23b5d5fc198a0ac8622d83696d31 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3865f2960e..f011992daa 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -717,7 +717,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt for (TouchedMonitor& gestureMonitor : gestureMonitors) { sp connection = getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); - if (connection->responsive) { + if (connection != nullptr && connection->responsive) { // This monitor could take more input. Drop all events preceding this // event, so that gesture monitor could get a chance to receive the stream ALOGW("Pruning the input queue because %s is unresponsive, but we have a " -- cgit v1.2.3-59-g8ed1b From 80d685aef18683ed175e0a3fddbd8f55a83c788d Mon Sep 17 00:00:00 2001 From: arthurhung Date: Fri, 19 Jun 2020 23:35:28 +0800 Subject: Clear spam log for INPUT_FEATURE_NO_INPUT_CHANNEL window Currently, in order to detect occlusion input, all buffered layers would update input info into InputFlinger, so the window may contain null token, we should check if id is also identical to prevent get the wrong result. Bug: 159349058 Test: Enable show tap, open bubble and watch logs. Change-Id: Ia1841ba51bce00fc9901fdf7324e8e31d9737082 --- .../inputflinger/dispatcher/InputDispatcher.cpp | 33 +++++++++++++--------- services/inputflinger/dispatcher/InputDispatcher.h | 3 +- 2 files changed, 21 insertions(+), 15 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f011992daa..cb68355e91 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3569,6 +3569,10 @@ std::vector> InputDispatcher::getWindowHandlesLocked( sp InputDispatcher::getWindowHandleLocked( const sp& windowHandleToken) const { + if (windowHandleToken == nullptr) { + return nullptr; + } + for (auto& it : mWindowHandlesByDisplay) { const std::vector> windowHandles = it.second; for (const sp& windowHandle : windowHandles) { @@ -3580,21 +3584,22 @@ sp InputDispatcher::getWindowHandleLocked( return nullptr; } -bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle) const { - for (auto& it : mWindowHandlesByDisplay) { - const std::vector> windowHandles = it.second; - for (const sp& handle : windowHandles) { - if (handle->getToken() == windowHandle->getToken()) { - if (windowHandle->getInfo()->displayId != it.first) { - ALOGE("Found window %s in display %" PRId32 - ", but it should belong to display %" PRId32, - windowHandle->getName().c_str(), it.first, - windowHandle->getInfo()->displayId); - } - return true; +bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle, + int32_t displayId) const { + const std::vector> windowHandles = getWindowHandlesLocked(displayId); + for (const sp& handle : windowHandles) { + if (handle->getId() == windowHandle->getId() && + handle->getToken() == windowHandle->getToken()) { + if (handle->getInfo()->displayId != displayId) { + ALOGE("Found window %s in display %" PRId32 + ", but it should belong to display %" PRId32, + windowHandle->getName().c_str(), displayId, + windowHandle->getInfo()->displayId); } + return true; } } + return false; } @@ -3754,7 +3759,7 @@ void InputDispatcher::setInputWindowsLocked( TouchState& state = stateIt->second; for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { + if (!hasWindowHandleLocked(touchedWindow.windowHandle, displayId)) { if (DEBUG_FOCUS) { ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); @@ -3778,7 +3783,7 @@ void InputDispatcher::setInputWindowsLocked( // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { + if (!hasWindowHandleLocked(oldWindowHandle, displayId)) { if (DEBUG_FOCUS) { ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e679c6b06f..0f558b4e73 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -301,7 +301,8 @@ private: sp getWindowHandleLocked(const sp& windowHandleToken) const REQUIRES(mLock); sp getInputChannelLocked(const sp& windowToken) const REQUIRES(mLock); - bool hasWindowHandleLocked(const sp& windowHandle) const REQUIRES(mLock); + bool hasWindowHandleLocked(const sp& windowHandle, int32_t displayId) const + REQUIRES(mLock); /* * Validate and update InputWindowHandles for a given display. -- cgit v1.2.3-59-g8ed1b From 017bcd179d70208bb8a3fcbe115f8cfa5e64b794 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Tue, 23 Jun 2020 19:12:00 +0000 Subject: Revert "Clear spam log for INPUT_FEATURE_NO_INPUT_CHANNEL window" This reverts commit 80d685aef18683ed175e0a3fddbd8f55a83c788d. Reason for revert: seeing if maybe caused regression (b/159719013) Bug: 159719013 Bug: 159349058 Change-Id: Iac21f799822bab5c26b9753ed78617e0087810bf --- .../inputflinger/dispatcher/InputDispatcher.cpp | 33 +++++++++------------- services/inputflinger/dispatcher/InputDispatcher.h | 3 +- 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index cb68355e91..f011992daa 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3569,10 +3569,6 @@ std::vector> InputDispatcher::getWindowHandlesLocked( sp InputDispatcher::getWindowHandleLocked( const sp& windowHandleToken) const { - if (windowHandleToken == nullptr) { - return nullptr; - } - for (auto& it : mWindowHandlesByDisplay) { const std::vector> windowHandles = it.second; for (const sp& windowHandle : windowHandles) { @@ -3584,22 +3580,21 @@ sp InputDispatcher::getWindowHandleLocked( return nullptr; } -bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle, - int32_t displayId) const { - const std::vector> windowHandles = getWindowHandlesLocked(displayId); - for (const sp& handle : windowHandles) { - if (handle->getId() == windowHandle->getId() && - handle->getToken() == windowHandle->getToken()) { - if (handle->getInfo()->displayId != displayId) { - ALOGE("Found window %s in display %" PRId32 - ", but it should belong to display %" PRId32, - windowHandle->getName().c_str(), displayId, - windowHandle->getInfo()->displayId); +bool InputDispatcher::hasWindowHandleLocked(const sp& windowHandle) const { + for (auto& it : mWindowHandlesByDisplay) { + const std::vector> windowHandles = it.second; + for (const sp& handle : windowHandles) { + if (handle->getToken() == windowHandle->getToken()) { + if (windowHandle->getInfo()->displayId != it.first) { + ALOGE("Found window %s in display %" PRId32 + ", but it should belong to display %" PRId32, + windowHandle->getName().c_str(), it.first, + windowHandle->getInfo()->displayId); + } + return true; } - return true; } } - return false; } @@ -3759,7 +3754,7 @@ void InputDispatcher::setInputWindowsLocked( TouchState& state = stateIt->second; for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle, displayId)) { + if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { if (DEBUG_FOCUS) { ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); @@ -3783,7 +3778,7 @@ void InputDispatcher::setInputWindowsLocked( // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (const sp& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle, displayId)) { + if (!hasWindowHandleLocked(oldWindowHandle)) { if (DEBUG_FOCUS) { ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 0f558b4e73..e679c6b06f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -301,8 +301,7 @@ private: sp getWindowHandleLocked(const sp& windowHandleToken) const REQUIRES(mLock); sp getInputChannelLocked(const sp& windowToken) const REQUIRES(mLock); - bool hasWindowHandleLocked(const sp& windowHandle, int32_t displayId) const - REQUIRES(mLock); + bool hasWindowHandleLocked(const sp& windowHandle) const REQUIRES(mLock); /* * Validate and update InputWindowHandles for a given display. -- cgit v1.2.3-59-g8ed1b From be737674afd67f67e7053d3c0c87ed5dc61f052d Mon Sep 17 00:00:00 2001 From: arthurhung Date: Wed, 24 Jun 2020 12:29:21 +0800 Subject: Clear spam log for INPUT_FEATURE_NO_INPUT_CHANNEL window Currently, in order to detect occlusion input, all buffered layers would update input info into InputFlinger, so the window may contain null token, we should check if id is also identical to prevent get the wrong result Bug: 159349058 Test: Enable show tap, open bubble and watch logs. Change-Id: I8412812e9b100988ab380ad64366f2548e50d9e5 --- services/inputflinger/dispatcher/InputDispatcher.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f011992daa..fe016af01f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3569,6 +3569,10 @@ std::vector> InputDispatcher::getWindowHandlesLocked( sp InputDispatcher::getWindowHandleLocked( const sp& windowHandleToken) const { + if (windowHandleToken == nullptr) { + return nullptr; + } + for (auto& it : mWindowHandlesByDisplay) { const std::vector> windowHandles = it.second; for (const sp& windowHandle : windowHandles) { @@ -3584,7 +3588,8 @@ bool InputDispatcher::hasWindowHandleLocked(const sp& windowH for (auto& it : mWindowHandlesByDisplay) { const std::vector> windowHandles = it.second; for (const sp& handle : windowHandles) { - if (handle->getToken() == windowHandle->getToken()) { + if (handle->getId() == windowHandle->getId() && + handle->getToken() == windowHandle->getToken()) { if (windowHandle->getInfo()->displayId != it.first) { ALOGE("Found window %s in display %" PRId32 ", but it should belong to display %" PRId32, -- cgit v1.2.3-59-g8ed1b From 7a376674ad3b25ca99f28833a310f1b5d4703f8e Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 26 Jun 2020 20:51:44 +0100 Subject: Move PointerController from sp to shared_ptr Bug: 160010896 Test: atest PointerController_test, atest InputReader_test, manual usage Change-Id: Ic43ab94ca76c736fde0d217efeab99b2afd6f622 Merged-In: Ic43ab94ca76c736fde0d217efeab99b2afd6f622 (cherry picked from commit 17db18e83fb3d4cdba0dcf25c2bcec350fab3d16) --- services/inputflinger/include/InputReaderBase.h | 3 +- .../include/PointerControllerInterface.h | 2 +- services/inputflinger/reader/InputReader.cpp | 12 +++--- services/inputflinger/reader/include/InputReader.h | 20 +++++---- .../reader/include/InputReaderContext.h | 2 +- .../inputflinger/reader/mapper/CursorInputMapper.h | 2 +- .../reader/mapper/TouchCursorInputMapperCommon.h | 5 ++- .../reader/mapper/TouchInputMapper.cpp | 2 +- .../inputflinger/reader/mapper/TouchInputMapper.h | 2 +- services/inputflinger/tests/InputReader_test.cpp | 50 +++++++++++----------- 10 files changed, 54 insertions(+), 46 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index f8d535152e..0fa878759b 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -335,7 +335,8 @@ public: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0; /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ - virtual sp obtainPointerController(int32_t deviceId) = 0; + virtual std::shared_ptr obtainPointerController( + int32_t deviceId) = 0; /* Notifies the input reader policy that some input devices have changed * and provides information about all current input devices. diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 194c66547f..bffff881b2 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -33,7 +33,7 @@ namespace android { * The pointer controller is responsible for providing synchronization and for tracking * display orientation changes if needed. */ -class PointerControllerInterface : public virtual RefBase { +class PointerControllerInterface { protected: PointerControllerInterface() { } virtual ~PointerControllerInterface() { } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 657a134865..d3441acd2b 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -390,8 +390,9 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32 } } -sp InputReader::getPointerControllerLocked(int32_t deviceId) { - sp controller = mPointerController.promote(); +std::shared_ptr InputReader::getPointerControllerLocked( + int32_t deviceId) { + std::shared_ptr controller = mPointerController.lock(); if (controller == nullptr) { controller = mPolicy->obtainPointerController(deviceId); mPointerController = controller; @@ -401,7 +402,7 @@ sp InputReader::getPointerControllerLocked(int32_t d } void InputReader::updatePointerDisplayLocked() { - sp controller = mPointerController.promote(); + std::shared_ptr controller = mPointerController.lock(); if (controller == nullptr) { return; } @@ -424,7 +425,7 @@ void InputReader::updatePointerDisplayLocked() { } void InputReader::fadePointerLocked() { - sp controller = mPointerController.promote(); + std::shared_ptr controller = mPointerController.lock(); if (controller != nullptr) { controller->fade(PointerControllerInterface::TRANSITION_GRADUAL); } @@ -725,7 +726,8 @@ void InputReader::ContextImpl::fadePointer() { mReader->fadePointerLocked(); } -sp InputReader::ContextImpl::getPointerController(int32_t deviceId) { +std::shared_ptr InputReader::ContextImpl::getPointerController( + int32_t deviceId) { // lock is already held by the input loop return mReader->getPointerControllerLocked(deviceId); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 693ec30b7d..108b9c236c 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -17,19 +17,20 @@ #ifndef _UI_INPUTREADER_INPUT_READER_H #define _UI_INPUTREADER_INPUT_READER_H -#include "EventHub.h" -#include "InputListener.h" -#include "InputReaderBase.h" -#include "InputReaderContext.h" -#include "InputThread.h" - #include #include #include +#include #include #include +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" +#include "InputThread.h" + namespace android { class InputDevice; @@ -104,7 +105,8 @@ protected: virtual void disableVirtualKeysUntil(nsecs_t time) override; virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; virtual void fadePointer() override; - virtual sp getPointerController(int32_t deviceId) override; + virtual std::shared_ptr getPointerController( + int32_t deviceId) override; virtual void requestTimeoutAtTime(nsecs_t when) override; virtual int32_t bumpGeneration() override; virtual void getExternalStylusDevices(std::vector& outDevices) override; @@ -160,8 +162,8 @@ private: void dispatchExternalStylusState(const StylusState& state); // The PointerController that is shared among all the input devices that need it. - wp mPointerController; - sp getPointerControllerLocked(int32_t deviceId); + std::weak_ptr mPointerController; + std::shared_ptr getPointerControllerLocked(int32_t deviceId); void updatePointerDisplayLocked(); void fadePointerLocked(); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 85701e4f63..ffb8d8c44a 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -46,7 +46,7 @@ public: virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0; virtual void fadePointer() = 0; - virtual sp getPointerController(int32_t deviceId) = 0; + virtual std::shared_ptr getPointerController(int32_t deviceId) = 0; virtual void requestTimeoutAtTime(nsecs_t when) = 0; virtual int32_t bumpGeneration() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index f65ac3934a..05bbb26716 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -106,7 +106,7 @@ private: int32_t mOrientation; - sp mPointerController; + std::shared_ptr mPointerController; int32_t mButtonState; nsecs_t mDownTime; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 2a3e2637f1..a86443dee1 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -17,12 +17,13 @@ #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#include +#include + #include "EventHub.h" #include "InputListener.h" #include "InputReaderContext.h" -#include - namespace android { // --- Static Definitions --- diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 99a572a5fd..10b6a180fb 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -765,7 +765,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPointerController = getContext()->getPointerController(getDeviceId()); } } else { - mPointerController.clear(); + mPointerController.reset(); } if (viewportChanged || deviceModeChanged) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 58bfc5c596..7f811a0e1b 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -376,7 +376,7 @@ protected: nsecs_t mDownTime; // The pointer controller, or null if the device is not a pointer. - sp mPointerController; + std::shared_ptr mPointerController; std::vector mVirtualKeys; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c457a1525f..18bd3d06e1 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -27,12 +27,13 @@ #include #include #include - #include #include #include #include +#include + namespace android { using std::chrono_literals::operator""ms; @@ -76,15 +77,14 @@ class FakePointerController : public PointerControllerInterface { int32_t mButtonState; int32_t mDisplayId; -protected: - virtual ~FakePointerController() { } - public: FakePointerController() : mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0), mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) { } + virtual ~FakePointerController() {} + void setBounds(float minX, float minY, float maxX, float maxY) { mHaveBounds = true; mMinX = minX; @@ -176,7 +176,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { std::condition_variable mDevicesChangedCondition; InputReaderConfiguration mConfig; - KeyedVector > mPointerControllers; + std::unordered_map> mPointerControllers; std::vector mInputDevices GUARDED_BY(mLock); bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector mViewports; @@ -256,8 +256,8 @@ public: void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } - void setPointerController(int32_t deviceId, const sp& controller) { - mPointerControllers.add(deviceId, controller); + void setPointerController(int32_t deviceId, std::shared_ptr controller) { + mPointerControllers.insert_or_assign(deviceId, std::move(controller)); } const InputReaderConfiguration* getReaderConfiguration() const { @@ -318,8 +318,8 @@ private: *outConfig = mConfig; } - virtual sp obtainPointerController(int32_t deviceId) { - return mPointerControllers.valueFor(deviceId); + virtual std::shared_ptr obtainPointerController(int32_t deviceId) { + return mPointerControllers[deviceId]; } virtual void notifyInputDevicesChanged(const std::vector& inputDevices) { @@ -847,7 +847,7 @@ class FakeInputReaderContext : public InputReaderContext { bool mUpdateGlobalMetaStateWasCalled; int32_t mGeneration; int32_t mNextId; - wp mPointerController; + std::weak_ptr mPointerController; public: FakeInputReaderContext(std::shared_ptr eventHub, @@ -876,7 +876,7 @@ public: } void updatePointerDisplay() { - sp controller = mPointerController.promote(); + std::shared_ptr controller = mPointerController.lock(); if (controller != nullptr) { InputReaderConfiguration config; mPolicy->getReaderConfiguration(&config); @@ -913,8 +913,8 @@ private: virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; } - virtual sp getPointerController(int32_t deviceId) { - sp controller = mPointerController.promote(); + virtual std::shared_ptr getPointerController(int32_t deviceId) { + std::shared_ptr controller = mPointerController.lock(); if (controller == nullptr) { controller = mPolicy->obtainPointerController(deviceId); mPointerController = controller; @@ -2348,9 +2348,9 @@ protected: ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON); } - static void assertPosition(const sp& controller, float x, float y) { + static void assertPosition(const FakePointerController& controller, float x, float y) { float actualX, actualY; - controller->getPosition(&actualX, &actualY); + controller.getPosition(&actualX, &actualY); ASSERT_NEAR(x, actualX, 1); ASSERT_NEAR(y, actualY, 1); } @@ -3021,12 +3021,12 @@ class CursorInputMapperTest : public InputMapperTest { protected: static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - sp mFakePointerController; + std::shared_ptr mFakePointerController; virtual void SetUp() override { InputMapperTest::SetUp(); - mFakePointerController = new FakePointerController(); + mFakePointerController = std::make_shared(); mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController); } @@ -3682,7 +3682,7 @@ TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerArou ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } TEST_F(CursorInputMapperTest, Process_PointerCapture) { @@ -3710,7 +3710,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f)); // Button press. process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); @@ -3749,7 +3749,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f)); // Disable pointer capture and check that the device generation got bumped // and events are generated the usual way. @@ -3770,7 +3770,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { @@ -3798,7 +3798,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId); } @@ -6806,7 +6806,8 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { // Setup for second display. - sp fakePointerController = new FakePointerController(); + std::shared_ptr fakePointerController = + std::make_shared(); fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); fakePointerController->setPosition(100, 200); fakePointerController->setButtonState(0); @@ -6866,7 +6867,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { device2->reset(ARBITRARY_TIME); // Setup PointerController. - sp fakePointerController = new FakePointerController(); + std::shared_ptr fakePointerController = + std::make_shared(); mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController); -- cgit v1.2.3-59-g8ed1b From 976db0c3a27fcfd8122087a0b1a3936609b98487 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 2 Jul 2020 00:00:29 +0100 Subject: Move PointerController enums to enum classes. Bug: 160010896 Test: atest PointerController_test, compile Change-Id: Ie5ba68a5082fff6ccc85ab01ff49344b73d0bb1b Merged-In: Ie5ba68a5082fff6ccc85ab01ff49344b73d0bb1b (cherry picked from commit ca5bede6d18f05e025b3c46c989091bad7895845) --- .../include/PointerControllerInterface.h | 12 +++++----- services/inputflinger/reader/InputReader.cpp | 2 +- .../reader/mapper/CursorInputMapper.cpp | 7 +++--- .../reader/mapper/TouchInputMapper.cpp | 26 +++++++++++----------- 4 files changed, 24 insertions(+), 23 deletions(-) (limited to 'services/inputflinger') diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index bffff881b2..85d7247915 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -59,11 +59,11 @@ public: /* Gets the absolute location of the pointer. */ virtual void getPosition(float* outX, float* outY) const = 0; - enum Transition { + enum class Transition { // Fade/unfade immediately. - TRANSITION_IMMEDIATE, + IMMEDIATE, // Fade/unfade gradually. - TRANSITION_GRADUAL, + GRADUAL, }; /* Fades the pointer out now. */ @@ -75,11 +75,11 @@ public: * wants to ensure that the pointer becomes visible again. */ virtual void unfade(Transition transition) = 0; - enum Presentation { + enum class Presentation { // Show the mouse pointer. - PRESENTATION_POINTER, + POINTER, // Show spots and a spot anchor in place of the mouse pointer. - PRESENTATION_SPOT, + SPOT, }; /* Sets the mode of the pointer controller. */ diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index d3441acd2b..06e374353c 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -427,7 +427,7 @@ void InputReader::updatePointerDisplayLocked() { void InputReader::fadePointerLocked() { std::shared_ptr controller = mPointerController.lock(); if (controller != nullptr) { - controller->fade(PointerControllerInterface::TRANSITION_GRADUAL); + controller->fade(PointerControllerInterface::Transition::GRADUAL); } } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 887ab53c76..1a4d5517e2 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -20,6 +20,7 @@ #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" +#include "PointerControllerInterface.h" #include "TouchCursorInputMapperCommon.h" namespace android { @@ -154,7 +155,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mParameters.mode = Parameters::MODE_POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } else { ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); } @@ -316,7 +317,7 @@ void CursorInputMapper::sync(nsecs_t when) { float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { mPointerController->move(deltaX, deltaY); @@ -326,7 +327,7 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->setButtonState(currentButtonState); } - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 10b6a180fb..efdc84fd8f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1383,7 +1383,7 @@ void TouchInputMapper::reset(nsecs_t when) { resetExternalStylus(); if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->clearSpots(); } @@ -1589,8 +1589,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { } else { if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, @@ -2327,7 +2327,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Update the pointer presentation and spots. if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (finishPreviousGesture || cancelPreviousGesture) { mPointerController->clearSpots(); } @@ -2339,7 +2339,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag mPointerController->getDisplayId()); } } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); } // Show or hide the pointer if needed. @@ -2349,7 +2349,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL); } break; case PointerGesture::TAP: @@ -2360,15 +2360,15 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag case PointerGesture::SWIPE: // Unfade the pointer when the current gesture manipulates the // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); break; case PointerGesture::FREEFORM: // Fade the pointer when the current gesture manipulates a different // area and there are spots to guide the user experience. if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } break; } @@ -2537,7 +2537,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) // Remove any current spots. if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->clearSpots(); } } @@ -3396,12 +3396,12 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, int32_t displayId = mViewport.displayId; if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); mPointerController->clearSpots(); mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); } displayId = mPointerController->getDisplayId(); -- cgit v1.2.3-59-g8ed1b