Add last interacted window check in setInTouchMode
Allow touch mode switch if caller owns the focused window.
If no window is currently focused, then touch will be still allowed if
caller owned one of the previously interacted window.
Bug: 218541064
Test: atest inputflinger_tests
Test: atest InputDispatcherTouchModeChangedTests
Change-Id: I5cfa879a6e1358efe3781061c5712707da813967
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c8a3ccf..3d860f9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -567,6 +567,17 @@
return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy();
}
+bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int32_t uid) {
+ if (windowHandle == nullptr) {
+ return false;
+ }
+ const WindowInfo* windowInfo = windowHandle->getInfo();
+ if (pid == windowInfo->ownerPid && uid == windowInfo->ownerUid) {
+ return true;
+ }
+ return false;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -4990,21 +5001,11 @@
toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission));
}
if (!hasPermission) {
- const sp<IBinder> focusedToken =
- mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
-
- // TODO(b/218541064): if no window is currently focused, then we need to check the last
- // interacted window (within 1 second timeout). We should allow touch mode change
- // if the last interacted window owner's pid/uid match the calling ones.
- if (focusedToken == nullptr) {
- return false;
- }
- const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken);
- if (windowHandle == nullptr) {
- return false;
- }
- const WindowInfo* windowInfo = windowHandle->getInfo();
- if (pid != windowInfo->ownerPid || uid != windowInfo->ownerUid) {
+ if (!focusedWindowIsOwnedByLocked(pid, uid) &&
+ !recentWindowsAreOwnedByLocked(pid, uid)) {
+ ALOGD("Touch mode switch rejected, caller (pid=%d, uid=%d) doesn't own the focused "
+ "window nor none of the previously interacted window",
+ pid, uid);
return false;
}
}
@@ -5022,6 +5023,24 @@
return true;
}
+bool InputDispatcher::focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) {
+ const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+ if (focusedToken == nullptr) {
+ return false;
+ }
+ sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken);
+ return isWindowOwnedBy(windowHandle, pid, uid);
+}
+
+bool InputDispatcher::recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) {
+ return std::find_if(mInteractionConnectionTokens.begin(), mInteractionConnectionTokens.end(),
+ [&](const sp<IBinder>& connectionToken) REQUIRES(mLock) {
+ const sp<WindowInfoHandle> windowHandle =
+ getWindowHandleLocked(connectionToken);
+ return isWindowOwnedBy(windowHandle, pid, uid);
+ }) != mInteractionConnectionTokens.end();
+}
+
void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
if (opacity < 0 || opacity > 1) {
LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 3c79c98..ca24f17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -433,7 +433,8 @@
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
- // The connection tokens of the channels that the user last interacted, for debugging
+ // The connection tokens of the channels that the user last interacted (used for debugging and
+ // when switching touch mode state).
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
GUARDED_BY(mLock);
void updateInteractionTokensLocked(const EventEntry& entry,
@@ -677,6 +678,10 @@
void traceOutboundQueueLength(const Connection& connection);
void traceWaitQueueLength(const Connection& connection);
+ // Check window ownership
+ bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
+ bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
+
sp<InputReporterInterface> mReporter;
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bf58705..31e8d71 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6335,8 +6335,11 @@
mWindow->consumeFocusEvent(true);
// Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
- mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, INJECTOR_PID,
- INJECTOR_UID, /* hasPermission */ true);
+ if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, INJECTOR_PID,
+ INJECTOR_UID, /* hasPermission */ true)) {
+ mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
+ mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
+ }
}
void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
@@ -6381,6 +6384,23 @@
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
+ // Interact with the window first.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Then remove focus.
+ mWindow->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ // Assert that caller can switch touch mode by owning one of the last interacted window.
+ const WindowInfo& windowInfo = *mWindow->getInfo();
+ ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode,
+ windowInfo.ownerPid, windowInfo.ownerUid,
+ /* hasPermission= */ false));
+}
+
class InputDispatcherSpyWindowTest : public InputDispatcherTest {
public:
sp<FakeWindowHandle> createSpy() {