diff options
| -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() |