/* * 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 namespace android { // --- FakeInputReceiver --- FakeInputReceiver::FakeInputReceiver(std::unique_ptr clientChannel, const std::string name) : mConsumer(std::move(clientChannel)), mName(name) {} std::unique_ptr 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::unique_ptr> FakeInputReceiver::receiveEvent( std::chrono::milliseconds timeout) { uint32_t consumeSeq; std::unique_ptr 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(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 timeline) { const status_t status = mConsumer.sendTimeline(inputEventId, timeline); ASSERT_EQ(OK, status); } void FakeInputReceiver::consumeEvent(InputEventType expectedEventType, int32_t expectedAction, std::optional expectedDisplayId, std::optional expectedFlags) { std::unique_ptr 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(*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(*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 FakeInputReceiver::consumeMotion() { std::unique_ptr 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(static_cast(event.release())); } void FakeInputReceiver::consumeMotionEvent(const ::testing::Matcher& matcher) { std::unique_ptr 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 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(ui::LogicalDisplayId::INVALID, event->getDisplayId()) << mName.c_str() << ": event displayId should always be NONE."; FocusEvent& focusEvent = static_cast(*event); EXPECT_EQ(hasFocus, focusEvent.getHasFocus()); } void FakeInputReceiver::consumeCaptureEvent(bool hasCapture) { std::unique_ptr 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(ui::LogicalDisplayId::INVALID, event->getDisplayId()) << mName.c_str() << ": event displayId should always be NONE."; const auto& captureEvent = static_cast(*event); EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled()); } void FakeInputReceiver::consumeDragEvent(bool isExiting, float x, float y) { std::unique_ptr 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(ui::LogicalDisplayId::INVALID, event->getDisplayId()) << mName.c_str() << ": event displayId should always be NONE."; const auto& dragEvent = static_cast(*event); EXPECT_EQ(isExiting, dragEvent.isExiting()); EXPECT_EQ(x, dragEvent.getX()); EXPECT_EQ(y, dragEvent.getY()); } void FakeInputReceiver::consumeTouchModeEvent(bool inTouchMode) { std::unique_ptr 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(ui::LogicalDisplayId::INVALID, event->getDisplayId()) << mName.c_str() << ": event displayId should always be NONE."; const auto& touchModeEvent = static_cast(*event); EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); } void FakeInputReceiver::assertNoEvents(std::chrono::milliseconds timeout) { std::unique_ptr event = consume(timeout); if (event == nullptr) { return; } if (event->getType() == InputEventType::KEY) { KeyEvent& keyEvent = static_cast(*event); ADD_FAILURE() << "Received key event " << keyEvent; } else if (event->getType() == InputEventType::MOTION) { MotionEvent& motionEvent = static_cast(*event); ADD_FAILURE() << "Received motion event " << motionEvent; } else if (event->getType() == InputEventType::FOCUS) { FocusEvent& focusEvent = static_cast(*event); ADD_FAILURE() << "Received focus event, hasFocus = " << (focusEvent.getHasFocus() ? "true" : "false"); } else if (event->getType() == InputEventType::CAPTURE) { const auto& captureEvent = static_cast(*event); ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = " << (captureEvent.getPointerCaptureEnabled() ? "true" : "false"); } else if (event->getType() == InputEventType::TOUCH_MODE) { const auto& touchModeEvent = static_cast(*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 FakeInputReceiver::getToken() { return mConsumer.getChannel()->getConnectionToken(); } int FakeInputReceiver::getChannelFd() { return mConsumer.getChannel()->getFd(); } // --- FakeWindowHandle --- std::function&, const gui::WindowInfo&)> FakeWindowHandle::sOnEventReceivedCallback{}; std::atomic FakeWindowHandle::sId{1}; FakeWindowHandle::FakeWindowHandle( const std::shared_ptr& inputApplicationHandle, const std::unique_ptr& dispatcher, const std::string name, ui::LogicalDisplayId displayId, bool createInputChannel) : mName(name) { sp token; if (createInputChannel) { base::Result> channel = dispatcher->createInputChannel(name); token = (*channel)->getConnectionToken(); mInputReceiver = std::make_unique(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::clone(ui::LogicalDisplayId displayId) { sp handle = sp::make(mInfo.name + "(Mirror)"); handle->mInfo = mInfo; handle->mInfo.displayId = displayId; handle->mInfo.id = sId++; handle->mInputReceiver = mInputReceiver; return handle; } std::unique_ptr FakeWindowHandle::consumeKey(bool handled) { std::unique_ptr 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(static_cast(event.release())); } std::unique_ptr FakeWindowHandle::consumeMotionEvent( const ::testing::Matcher& matcher) { std::unique_ptr event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); if (event == nullptr) { std::ostringstream matcherDescription; matcher.DescribeTo(&matcherDescription); ADD_FAILURE() << "No event (expected " << matcherDescription.str() << ") on " << mName; return nullptr; } if (event->getType() != InputEventType::MOTION) { ADD_FAILURE() << "Instead of motion event, got " << *event << " on " << mName; return nullptr; } std::unique_ptr motionEvent = std::unique_ptr(static_cast(event.release())); if (motionEvent == nullptr) { return nullptr; } EXPECT_THAT(*motionEvent, matcher) << " on " << mName; return motionEvent; } void FakeWindowHandle::assertNoEvents(std::optional 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 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 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::unique_ptr> 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