Extract FakeInputReceiver and FakeInputWindow into a separate cpp file
It will be used by other tests that will be added in the future.
Bug: 210460522
Test: build
Change-Id: I2dfc9f2db73262e0e0a587940cbf513d697cc1c9
diff --git a/services/inputflinger/tests/FakeWindows.cpp b/services/inputflinger/tests/FakeWindows.cpp
new file mode 100644
index 0000000..0ac2f0f
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindows.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2024 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 "FakeWindows.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- FakeInputReceiver ---
+
+FakeInputReceiver::FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel,
+ const std::string name)
+ : mConsumer(std::move(clientChannel)), mName(name) {}
+
+std::unique_ptr<InputEvent> FakeInputReceiver::consume(std::chrono::milliseconds timeout,
+ bool handled) {
+ auto [consumeSeq, event] = receiveEvent(timeout);
+ if (!consumeSeq) {
+ return nullptr;
+ }
+ finishEvent(*consumeSeq, handled);
+ return std::move(event);
+}
+
+std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> FakeInputReceiver::receiveEvent(
+ std::chrono::milliseconds timeout) {
+ uint32_t consumeSeq;
+ std::unique_ptr<InputEvent> event;
+
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t status = WOULD_BLOCK;
+ while (status == WOULD_BLOCK) {
+ InputEvent* rawEventPtr = nullptr;
+ status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &rawEventPtr);
+ event = std::unique_ptr<InputEvent>(rawEventPtr);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > timeout) {
+ break;
+ }
+ }
+
+ if (status == WOULD_BLOCK) {
+ // Just means there's no event available.
+ return std::make_pair(std::nullopt, nullptr);
+ }
+
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+ return std::make_pair(std::nullopt, nullptr);
+ }
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+ }
+ return std::make_pair(consumeSeq, std::move(event));
+}
+
+void FakeInputReceiver::finishEvent(uint32_t consumeSeq, bool handled) {
+ const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled);
+ ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+}
+
+void FakeInputReceiver::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ const status_t status = mConsumer.sendTimeline(inputEventId, timeline);
+ ASSERT_EQ(OK, status);
+}
+
+void FakeInputReceiver::consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
+ std::optional<int32_t> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(expectedEventType, event->getType())
+ << mName.c_str() << " expected " << ftl::enum_string(expectedEventType)
+ << " event, got " << *event;
+
+ if (expectedDisplayId.has_value()) {
+ EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ }
+
+ switch (expectedEventType) {
+ case InputEventType::KEY: {
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+ ASSERT_THAT(keyEvent, WithKeyAction(expectedAction));
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
+ }
+ break;
+ }
+ case InputEventType::MOTION: {
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ ASSERT_THAT(motionEvent, WithMotionAction(expectedAction));
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
+ }
+ break;
+ }
+ case InputEventType::FOCUS: {
+ FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
+ }
+ case InputEventType::CAPTURE: {
+ FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
+ }
+ case InputEventType::TOUCH_MODE: {
+ FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
+ }
+ case InputEventType::DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
+ }
+}
+
+std::unique_ptr<MotionEvent> FakeInputReceiver::consumeMotion() {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+
+ if (event == nullptr) {
+ ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
+ return nullptr;
+ }
+
+ if (event->getType() != InputEventType::MOTION) {
+ ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event;
+ return nullptr;
+ }
+ return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
+}
+
+void FakeInputReceiver::consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ std::unique_ptr<MotionEvent> motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
+ ASSERT_THAT(*motionEvent, matcher);
+}
+
+void FakeInputReceiver::consumeFocusEvent(bool hasFocus, bool inTouchMode) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::FOCUS, event->getType()) << "Instead of FocusEvent, got " << *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());
+}
+
+void FakeInputReceiver::consumeCaptureEvent(bool hasCapture) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::CAPTURE, event->getType())
+ << "Instead of CaptureEvent, got " << *event;
+
+ ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& captureEvent = static_cast<const CaptureEvent&>(*event);
+ EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
+}
+
+void FakeInputReceiver::consumeDragEvent(bool isExiting, float x, float y) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
+
+ EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+}
+
+void FakeInputReceiver::consumeTouchModeEvent(bool inTouchMode) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
+ << "Instead of TouchModeEvent, got " << *event;
+
+ ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+ const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+ EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
+}
+
+void FakeInputReceiver::assertNoEvents(std::chrono::milliseconds timeout) {
+ std::unique_ptr<InputEvent> event = consume(timeout);
+ if (event == nullptr) {
+ return;
+ }
+ if (event->getType() == InputEventType::KEY) {
+ KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+ ADD_FAILURE() << "Received key event " << keyEvent;
+ } else if (event->getType() == InputEventType::MOTION) {
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ ADD_FAILURE() << "Received motion event " << motionEvent;
+ } else if (event->getType() == InputEventType::FOCUS) {
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ ADD_FAILURE() << "Received focus event, hasFocus = "
+ << (focusEvent.getHasFocus() ? "true" : "false");
+ } else if (event->getType() == InputEventType::CAPTURE) {
+ const auto& captureEvent = static_cast<CaptureEvent&>(*event);
+ ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
+ << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
+ } else if (event->getType() == InputEventType::TOUCH_MODE) {
+ const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
+ ADD_FAILURE() << "Received touch mode event, inTouchMode = "
+ << (touchModeEvent.isInTouchMode() ? "true" : "false");
+ }
+ FAIL() << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
+}
+
+sp<IBinder> FakeInputReceiver::getToken() {
+ return mConsumer.getChannel()->getConnectionToken();
+}
+
+int FakeInputReceiver::getChannelFd() {
+ return mConsumer.getChannel()->getFd();
+}
+
+// --- FakeWindowHandle ---
+
+std::function<void(const std::unique_ptr<InputEvent>&, const gui::WindowInfo&)>
+ FakeWindowHandle::sOnEventReceivedCallback{};
+
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+FakeWindowHandle::FakeWindowHandle(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const std::unique_ptr<inputdispatcher::InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId, bool createInputChannel)
+ : mName(name) {
+ sp<IBinder> token;
+ if (createInputChannel) {
+ base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputChannel(name);
+ token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+ }
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.token = token;
+ mInfo.id = sId++;
+ mInfo.name = name;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
+ mInfo.frame = Rect(0, 0, WIDTH, HEIGHT);
+ mInfo.transform.set(0, 0);
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
+ mInfo.displayId = displayId;
+ mInfo.inputConfig = InputConfig::DEFAULT;
+}
+
+sp<FakeWindowHandle> FakeWindowHandle::clone(int32_t displayId) {
+ sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
+ handle->mInfo = mInfo;
+ handle->mInfo.displayId = displayId;
+ handle->mInfo.id = sId++;
+ handle->mInputReceiver = mInputReceiver;
+ return handle;
+}
+
+std::unique_ptr<KeyEvent> FakeWindowHandle::consumeKey(bool handled) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
+ if (event == nullptr) {
+ ADD_FAILURE() << "No event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << event;
+ return nullptr;
+ }
+ return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release()));
+}
+
+std::unique_ptr<MotionEvent> FakeWindowHandle::consumeMotionEvent(
+ const ::testing::Matcher<MotionEvent>& matcher) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ ADD_FAILURE() << "No event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::MOTION) {
+ ADD_FAILURE() << "Instead of motion event, got " << *event;
+ return nullptr;
+ }
+ std::unique_ptr<MotionEvent> motionEvent =
+ std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
+ EXPECT_THAT(*motionEvent, matcher);
+ return motionEvent;
+}
+
+void FakeWindowHandle::assertNoEvents(std::optional<std::chrono::milliseconds> timeout) {
+ if (mInputReceiver == nullptr && mInfo.inputConfig.test(InputConfig::NO_INPUT_CHANNEL)) {
+ return; // Can't receive events if the window does not have input channel
+ }
+ ASSERT_NE(nullptr, mInputReceiver)
+ << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL";
+ mInputReceiver->assertNoEvents(timeout.value_or(CONSUME_TIMEOUT_NO_EVENT_EXPECTED));
+}
+
+std::unique_ptr<InputEvent> FakeWindowHandle::consume(std::chrono::milliseconds timeout,
+ bool handled) {
+ if (mInputReceiver == nullptr) {
+ LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
+ }
+ std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled);
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed: no event";
+ }
+
+ if (sOnEventReceivedCallback != nullptr) {
+ sOnEventReceivedCallback(event, mInfo);
+ }
+ return event;
+}
+
+std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>>
+FakeWindowHandle::receive() {
+ if (mInputReceiver == nullptr) {
+ ADD_FAILURE() << "Invalid receive event on window with no receiver";
+ return std::make_pair(std::nullopt, nullptr);
+ }
+ auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ const auto& [_, event] = out;
+
+ if (sOnEventReceivedCallback != nullptr) {
+ sOnEventReceivedCallback(event, mInfo);
+ }
+ return out;
+}
+
+} // namespace android