Add InputDispatcher unit tests for untrusted touches [1/n]
Start off with the simple cases.
Test: atest inputflinger_tests
Bug: 158002302
Bug: 177840583
Change-Id: I5b63dd5089a579e7a1d537a1825c1df640d094c9
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 01945db..79a6774 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -31,6 +31,7 @@
using android::base::StringPrintf;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
+using android::os::TouchOcclusionMode;
using namespace android::flag_operators;
namespace android::inputdispatcher {
@@ -727,8 +728,9 @@
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+ std::optional<int32_t> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
InputEvent* event = consume();
ASSERT_NE(nullptr, event) << mName.c_str()
@@ -737,19 +739,25 @@
<< mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
- EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ if (expectedDisplayId.has_value()) {
+ EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ }
switch (expectedEventType) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
EXPECT_EQ(expectedAction, keyEvent.getAction());
- EXPECT_EQ(expectedFlags, keyEvent.getFlags());
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
+ }
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
EXPECT_EQ(expectedAction, motionEvent.getAction());
- EXPECT_EQ(expectedFlags, motionEvent.getFlags());
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
+ }
break;
}
case AINPUT_EVENT_TYPE_FOCUS: {
@@ -854,6 +862,7 @@
mInfo.name = name;
mInfo.type = InputWindowInfo::Type::APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
mInfo.frameLeft = 0;
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
@@ -883,6 +892,12 @@
void setPaused(bool paused) { mInfo.paused = paused; }
+ void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+ void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) {
+ mInfo.touchOcclusionMode = mode;
+ }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
@@ -928,6 +943,11 @@
void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
+ consumeAnyMotionDown(expectedDisplayId, expectedFlags);
+ }
+
+ void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
+ std::optional<int32_t> expectedFlags = std::nullopt) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
}
@@ -971,8 +991,9 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+ std::optional<int32_t> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
expectedFlags);
@@ -4240,4 +4261,112 @@
mSecondWindow->consumeCaptureEvent(true);
}
+class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
+protected:
+ constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
+ static const int32_t APP_A_UID = 10001;
+ static const int32_t APP_B_UID = 10002;
+
+ sp<FakeWindowHandle> mTouchWindow;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mTouchWindow = getWindow(APP_A_UID, "Touched");
+ mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
+ mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+ mTouchWindow.clear();
+ }
+
+ sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name,
+ os::TouchOcclusionMode mode) {
+ sp<FakeWindowHandle> window = getWindow(uid, name);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE);
+ window->setTouchOcclusionMode(mode);
+ return window;
+ }
+
+ sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
+ // Generate an arbitrary PID based on the UID
+ window->setOwnerInfo(1777 + (uid % 10000), uid);
+ return window;
+ }
+
+ void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
+ NotifyMotionArgs args =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, points);
+ mDispatcher->notifyMotion(&args);
+ }
+};
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, TouchOutsideOccludingWindow_AllowsTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ window->setFrame(Rect(0, 0, 50, 50));
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch({PointF{100, 100}});
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_A_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
+ window->setAlpha(0.7);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
+ const sp<FakeWindowHandle>& window =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
+ window->setAlpha(0.9);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher