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 --- services/inputflinger/InputManager.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') 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(); -- 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/InputManager.cpp') 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 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/InputManager.cpp') 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 ba266f24e580de2d8339050b62fad7e66441f843 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 3 Sep 2019 17:11:27 -0700 Subject: Let InputReader handle its own thread Previously, InputManager created and managed the InputReaderThread, which interacted with InputReader through the loopOnce() method in InputReaderInterface. We move the threading logic from InputManager to InputReader by removing the loopOnce() method from InputReaderInterface and adding a start() and stop() method in its place. Once InputReader is created, InputManager will call InputReaderInterface::start(), which creates and starts InputReader's own thread. InputManager can also call InputReaderInterface::stop() to halt further processing on InputReader's thread. Bug: 130819454 Test: atest inputflinger_tests Test: Touch input works on crosshatch Change-Id: Ic732436d4f00a831e317be1f16ac106a11652cff --- 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 | 44 ++++++++------------ 7 files changed, 108 insertions(+), 81 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7d3067242a..34a04dfd95 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 2b3257d132..59f275f58d 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,8 +1344,6 @@ protected: mFakeEventHub->addConfigurationMap(deviceId, configuration); } mFakeEventHub->finishDeviceScan(); - mReader->loopOnce(); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } @@ -1422,7 +1418,6 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { ASSERT_EQ(device->isEnabled(), true); disableDevice(deviceId, device); - mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); @@ -1430,13 +1425,11 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { 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); @@ -1560,7 +1553,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 +1562,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 +1594,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; @@ -1642,7 +1631,6 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { 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(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); // Device should only dispatch to the specified display. @@ -1674,7 +1662,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() { + virtual void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1688,7 +1676,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; @@ -1912,7 +1900,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 +1914,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() { + virtual void TearDown() override { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2589,7 +2577,7 @@ protected: sp mFakePointerController; - virtual void SetUp() { + virtual void SetUp() override { InputMapperTest::SetUp(); mFakePointerController = new FakePointerController(); -- cgit v1.2.3-59-g8ed1b From 95a4ed6e84e9b8845359f601050218fa28459f4b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 1 Nov 2019 05:53:44 +0000 Subject: Revert "Let InputReader handle its own thread" This reverts commit ba266f24e580de2d8339050b62fad7e66441f843. Reason for revert: b/143735040 looks like this CL breaks input tests Change-Id: I8c19046935d2bdaf9df5df97b2eff43308065c72 --- 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 | 44 ++++++++++++-------- 7 files changed, 81 insertions(+), 108 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 34a04dfd95..7d3067242a 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 59f275f58d..2b3257d132 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,6 +1346,8 @@ protected: mFakeEventHub->addConfigurationMap(deviceId, configuration); } mFakeEventHub->finishDeviceScan(); + mReader->loopOnce(); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); } @@ -1418,6 +1422,7 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { ASSERT_EQ(device->isEnabled(), true); disableDevice(deviceId, device); + mReader->loopOnce(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); @@ -1425,11 +1430,13 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { 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); @@ -1553,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; @@ -1562,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; @@ -1594,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; @@ -1631,6 +1642,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { 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(); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled()); // Device should only dispatch to the specified display. @@ -1662,7 +1674,7 @@ protected: InputDevice* mDevice; - virtual void SetUp() override { + virtual void SetUp() { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1676,7 +1688,7 @@ protected: DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } - virtual void TearDown() override { + virtual void TearDown() { delete mDevice; delete mFakeContext; @@ -1900,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(); @@ -1914,7 +1926,7 @@ protected: mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0); } - virtual void TearDown() override { + virtual void TearDown() { delete mDevice; delete mFakeContext; mFakeListener.clear(); @@ -2577,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 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/InputManager.cpp') 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/InputManager.cpp') 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 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/InputManager.cpp') 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/InputManager.cpp') 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 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/InputManager.cpp') 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/InputManager.cpp') 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/InputManager.cpp') 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/InputManager.cpp') 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