diff options
| author | 2019-09-20 16:32:01 +0100 | |
|---|---|---|
| committer | 2020-01-10 11:13:21 -0800 | |
| commit | f1035d4f7da852c63cb91ebaed863836b6a23f07 (patch) | |
| tree | 00d8ec19d8732363fc3fc00e48064f011bc91d08 | |
| parent | 838817f1953feb7db7c927333b0abd3979daef41 (diff) | |
Move focus dispatch to input (1/2)
Input now tells the apps when they have focus.
When a focus change has been detected, we enqueue a FocusEntry into the
dispatcher queue. It will be sent to the app via the socket.
At the same time, we tell the apps the current state of the touch mode.
Bug: 70668286
Test: atest libinput_tests inputflinger_tests
Change-Id: Icefb9383ba8424162d739c9e981bd1dd01cc7941
| -rw-r--r-- | libs/gui/tests/EndToEndNativeInputTest.cpp | 68 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.cpp | 15 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.h | 21 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 54 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 4 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 162 |
6 files changed, 300 insertions, 24 deletions
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 8d36ba7b70..04749e6d13 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,22 +112,31 @@ public: if (consumed != OK) { return nullptr; } - mInputConsumer->sendFinishedSignal(seqId, true); + status_t status = mInputConsumer->sendFinishedSignal(seqId, true); + EXPECT_EQ(OK, status) << "Could not send finished signal"; return ev; } + void assertFocusChange(bool hasFocus) { + InputEvent *ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, ev->getType()); + FocusEvent *focusEvent = static_cast<FocusEvent *>(ev); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + } + void expectTap(int x, int y) { InputEvent* ev = consumeEvent(); - EXPECT_TRUE(ev != nullptr); - EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); MotionEvent* mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); EXPECT_EQ(x, mev->getX(0)); EXPECT_EQ(y, mev->getY(0)); ev = consumeEvent(); - EXPECT_TRUE(ev != nullptr); - EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType()); mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } @@ -212,7 +221,7 @@ public: ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); const auto display = mComposerClient->getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); + ASSERT_NE(display, nullptr); DisplayInfo info; ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info)); @@ -259,18 +268,28 @@ void injectTap(int x, int y) { TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); - EXPECT_TRUE(surface->consumeEvent() != nullptr); + EXPECT_NE(surface->consumeEvent(), nullptr); } +/** + * Set up two surfaces side-by-side. Tap each surface. + * Next, swap the positions of the two surfaces. Inject tap into the two + * original locations. Ensure that the tap is received by the surfaces in the + * reverse order. + */ TEST_F(InputSurfacesTest, input_respects_positioning) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); + surface->assertFocusChange(true); std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface2->showAt(200, 200); + surface->assertFocusChange(false); + surface2->assertFocusChange(true); injectTap(201, 201); surface2->expectTap(1, 1); @@ -297,11 +316,16 @@ TEST_F(InputSurfacesTest, input_respects_layering) { std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface->showAt(10, 10); + surface->assertFocusChange(true); surface2->showAt(10, 10); + surface->assertFocusChange(false); + surface2->assertFocusChange(true); surface->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); + surface2->assertFocusChange(false); + surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -309,6 +333,8 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); + surface2->assertFocusChange(true); + surface->assertFocusChange(false); injectTap(11, 11); surface2->expectTap(1, 1); @@ -316,6 +342,8 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + surface2->assertFocusChange(false); + surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -328,9 +356,12 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); + bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); + fgSurface->assertFocusChange(true); + bgSurface->assertFocusChange(false); injectTap(106, 106); fgSurface->expectTap(1, 1); @@ -344,9 +375,12 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); + parentSurface->assertFocusChange(true); childSurface->mInputInfo.surfaceInset = 10; childSurface->showAt(100, 100); + childSurface->assertFocusChange(true); + parentSurface->assertFocusChange(false); childSurface->doTransaction([&](auto &t, auto &sc) { t.setPosition(sc, -5, -5); @@ -365,9 +399,12 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); + bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); + bgSurface->assertFocusChange(false); + fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -384,6 +421,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); + fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -400,6 +438,7 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -414,7 +453,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + bufferSurface->assertFocusChange(true); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -431,7 +473,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { postBuffer(bufferSurface->mSurfaceControl); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); + bufferSurface->assertFocusChange(true); + bgSurface->assertFocusChange(false); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -447,7 +492,10 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); fgSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + fgSurface->assertFocusChange(true); injectTap(11, 11); fgSurface->expectTap(1, 1); @@ -464,12 +512,17 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); + bgSurface->assertFocusChange(true); containerSurface->showAt(10, 10); + bgSurface->assertFocusChange(false); + containerSurface->assertFocusChange(true); injectTap(11, 11); containerSurface->expectTap(1, 1); containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + containerSurface->assertFocusChange(false); + bgSurface->assertFocusChange(true); injectTap(11, 11); bgSurface->expectTap(1, 1); @@ -478,6 +531,7 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(-1, -1); + surface->assertFocusChange(true); injectTap(0, 0); surface->expectTap(1, 1); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index e925f5b6ba..b7236547de 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -111,6 +111,21 @@ void DeviceResetEntry::appendDescription(std::string& msg) const { msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); } +// --- FocusEntry --- + +// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries +FocusEntry::FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken, + bool hasFocus) + : EventEntry(sequenceNum, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER), + connectionToken(connectionToken), + hasFocus(hasFocus) {} + +FocusEntry::~FocusEntry() {} + +void FocusEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); +} + // --- KeyEntry --- KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 9dcaadc6ee..e8c37f0e6e 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -33,7 +33,13 @@ namespace android::inputdispatcher { constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; struct EventEntry { - enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION }; + enum class Type { + CONFIGURATION_CHANGED, + DEVICE_RESET, + FOCUS, + KEY, + MOTION, + }; static const char* typeToString(Type type) { switch (type) { @@ -41,6 +47,8 @@ struct EventEntry { return "CONFIGURATION_CHANGED"; case Type::DEVICE_RESET: return "DEVICE_RESET"; + case Type::FOCUS: + return "FOCUS"; case Type::KEY: return "KEY"; case Type::MOTION: @@ -102,6 +110,17 @@ protected: virtual ~DeviceResetEntry(); }; +struct FocusEntry : EventEntry { + sp<IBinder> connectionToken; + bool hasFocus; + + FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~FocusEntry(); +}; + struct KeyEntry : EventEntry { int32_t deviceId; uint32_t source; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a3cb4f8f68..ce7399ef6e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -469,6 +469,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } + case EventEntry::Type::FOCUS: { + FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent); + dispatchFocusLocked(currentTime, typedEntry); + done = true; + dropReason = DropReason::NOT_DROPPED; // focus events are never dropped + break; + } + case EventEntry::Type::KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); if (isAppSwitchDue) { @@ -573,7 +581,8 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { break; } case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: { + case EventEntry::Type::DEVICE_RESET: + case EventEntry::Type::FOCUS: { // nothing to do break; } @@ -712,6 +721,7 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type)); @@ -872,6 +882,25 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset return true; } +void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { + FocusEntry* focusEntry = + new FocusEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, now(), window.getToken(), hasFocus); + enqueueInboundEventLocked(focusEntry); +} + +void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) { + sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken); + if (channel == nullptr) { + return; // Window has gone away + } + InputTarget target; + target.inputChannel = channel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + entry->dispatchInProgress = true; + + dispatchEventLocked(currentTime, entry, {target}); +} + bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. @@ -1254,6 +1283,7 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { displayId = motionEntry.displayId; break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type)); @@ -1993,6 +2023,10 @@ std::string InputDispatcher::getApplicationWindowLabel( } void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { + if (eventEntry.type == EventEntry::Type::FOCUS) { + // Focus events are passed to apps, but do not represent user activity. + return; + } int32_t displayId = getTargetDisplayId(eventEntry); sp<InputWindowHandle> focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); @@ -2027,6 +2061,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { eventType = USER_ACTIVITY_EVENT_BUTTON; break; } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events are not user activity", @@ -2224,6 +2259,9 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio break; } + case EventEntry::Type::FOCUS: { + break; + } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", @@ -2359,6 +2397,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, reportTouchEventForStatistics(*motionEntry); break; } + case EventEntry::Type::FOCUS: { + FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry); + status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, + focusEntry->hasFocus, + mInTouchMode); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", @@ -2598,6 +2644,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( *cancelationEventEntry)); break; } + case EventEntry::Type::FOCUS: { + LOG_ALWAYS_FATAL("Canceling focus events is not supported"); + break; + } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", @@ -3394,6 +3444,7 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } mFocusedWindowHandlesByDisplay.erase(displayId); } @@ -3403,6 +3454,7 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& newFocusedWindowHandle->getName().c_str(), displayId); } mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; + enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } if (mFocusedDisplayId == displayId) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 50b52502a0..a4ba0dec6a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -157,6 +157,9 @@ private: // Cleans up input state when dropping an inbound event. void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock); + // Enqueues a focus event. + void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock); + // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); @@ -299,6 +302,7 @@ private: nsecs_t* nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock); void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5ffc89d210..c262abb229 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -482,12 +482,31 @@ public: EXPECT_EQ(expectedFlags, motionEvent.getFlags()); break; } + case AINPUT_EVENT_TYPE_FOCUS: { + FAIL() << "Use 'consumeFocusEvent' for FOCUS events"; + } default: { FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; } } } + void consumeFocusEvent(bool hasFocus, bool inTouchMode) { + InputEvent* event = consume(); + ASSERT_NE(nullptr, event) << mName.c_str() + << ": consumer should have returned non-NULL event."; + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType()) + << "Got " << inputEventTypeToString(event->getType()) + << " event instead of FOCUS event"; + + ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + FocusEvent* focusEvent = static_cast<FocusEvent*>(event); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); + } + void assertNoEvents() { InputEvent* event = consume(); ASSERT_EQ(nullptr, event) @@ -508,7 +527,6 @@ class FakeWindowHandle : public InputWindowHandle { public: static const int32_t WIDTH = 600; static const int32_t HEIGHT = 800; - const std::string mName; FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, const sp<InputDispatcher>& dispatcher, const std::string name, @@ -551,7 +569,7 @@ public: virtual bool updateInfo() { return true; } - void setFocus() { mInfo.hasFocus = true; } + void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; @@ -586,6 +604,12 @@ public: expectedFlags); } + void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { + ASSERT_NE(mInputReceiver, nullptr) + << "Cannot consume events from a window with no receiver"; + mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode); + } + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, int32_t expectedFlags) { ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; @@ -608,7 +632,10 @@ public: sp<IBinder> getToken() { return mInfo.token; } + const std::string& getName() { return mName; } + private: + const std::string mName; std::unique_ptr<FakeInputReceiver> mInputReceiver; }; @@ -759,10 +786,11 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - // Expect one focus window exist in display. - windowSecond->setFocus(); - + // Display should have only one focused window + windowSecond->setFocus(true); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + + windowSecond->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -782,10 +810,11 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first) - windowTop->setFocus(); - windowSecond->setFocus(); + windowTop->setFocus(true); + windowSecond->setFocus(true); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + windowTop->consumeFocusEvent(true); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; @@ -805,11 +834,12 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - windowTop->setFocus(); - windowSecond->setFocus(); + windowTop->setFocus(true); + windowSecond->setFocus(true); // Release channel for window is no longer valid. windowTop->releaseChannel(); mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT); + windowSecond->consumeFocusEvent(true); // Test inject a key down, should dispatch to a valid window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) @@ -849,9 +879,10 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { sp<FakeApplicationHandle> application = new FakeApplicationHandle(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFocus(); + window->setFocus(true); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyKey(&keyArgs); @@ -890,6 +921,59 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + window->consumeFocusEvent(true); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + + // Window should receive key down event. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); +} + +TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + mDispatcher->waitForIdle(); + + window->assertNoEvents(); +} + +// If a window is touchable, but does not have focus, it should receive motion events, but not keys +TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + + // Send key + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + // Send motion + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + + // Window should receive only the motion event + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); // Key event or focus event will not be received +} + class FakeMonitorReceiver { public: FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, @@ -946,9 +1030,10 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocus(); + window->setFocus(true); mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); @@ -1010,6 +1095,49 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } +/** + * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to + * the device default right away. In the test scenario, we check both the default value, + * and the action of enabling / disabling. + */ +TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocus(true); + + SCOPED_TRACE("Check default value of touch mode"); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + SCOPED_TRACE("Remove the window to trigger focus loss"); + window->setFocus(false); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); + + SCOPED_TRACE("Disable touch mode"); + mDispatcher->setInTouchMode(false); + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); + + SCOPED_TRACE("Remove the window to trigger focus loss"); + window->setFocus(false); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); + + SCOPED_TRACE("Enable touch mode again"); + mDispatcher->setInTouchMode(true); + window->setFocus(true); + mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + window->assertNoEvents(); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: @@ -1023,8 +1151,9 @@ public: // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); - windowInPrimary->setFocus(); + windowInPrimary->setFocus(true); mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT); + windowInPrimary->consumeFocusEvent(true); application2 = new FakeApplicationHandle(); windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2", @@ -1034,8 +1163,9 @@ public: mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); - windowInSecondary->setFocus(); + windowInSecondary->setFocus(true); mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID); + windowInSecondary->consumeFocusEvent(true); } virtual void TearDown() override { @@ -1094,6 +1224,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; windowInPrimary->assertNoEvents(); + windowInSecondary->consumeFocusEvent(false); windowInSecondary->assertNoEvents(); } @@ -1244,10 +1375,11 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mFocusedWindow->setFocus(); + mFocusedWindow->setFocus(true); // Expect one focus window exist in display. mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT); + mFocusedWindow->consumeFocusEvent(true); } virtual void TearDown() override { @@ -1350,7 +1482,7 @@ protected: void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, const std::vector<PointF>& points) { - std::string name = window->mName; + const std::string name = window->getName(); InputEvent* event = window->consume(); ASSERT_NE(nullptr, event) << name.c_str() |