Add InputDispatcherInterface::transferTouch api
The new api will allow the transfer of touch from the current
window (wherever that may be) to the specified window.
Bug: 177997773
Test: atest inputflinger_tests
Change-Id: I6acd17cfb6de0fa8466c73e99e27c0b283864eae
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9a43ed9..8ea71fe 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4741,6 +4741,40 @@
return true;
}
+// Binder call
+bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
+ sp<IBinder> fromToken;
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+ if (toWindowHandle == nullptr) {
+ ALOGW("Could not find window associated with token=%p", destChannelToken.get());
+ return false;
+ }
+
+ const int32_t displayId = toWindowHandle->getInfo()->displayId;
+
+ auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt == mTouchStatesByDisplay.end()) {
+ ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
+ displayId);
+ return false;
+ }
+
+ TouchState& state = touchStateIt->second;
+ if (state.windows.size() != 1) {
+ ALOGW("Cannot transfer touch state because there are %zu windows being touched",
+ state.windows.size());
+ return false;
+ }
+ const TouchedWindow& touchedWindow = state.windows[0];
+ fromToken = touchedWindow.windowHandle->getToken();
+ } // release lock
+
+ return transferTouchFocus(fromToken, destChannelToken);
+}
+
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
if (DEBUG_FOCUS) {
ALOGD("Resetting and dropping all events (%s).", reason);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7ba03e8..6edc5f1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,60 +81,60 @@
*/
class InputDispatcher : public android::InputDispatcherInterface {
protected:
- virtual ~InputDispatcher();
+ ~InputDispatcher() override;
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
- virtual bool waitForIdle() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ void dump(std::string& dump) override;
+ void monitor() override;
+ bool waitForIdle() override;
+ status_t start() override;
+ status_t stop() override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
- virtual void notifyKey(const NotifyKeyArgs* args) override;
- virtual void notifyMotion(const NotifyMotionArgs* args) override;
- virtual void notifySwitch(const NotifySwitchArgs* args) override;
- virtual void notifySensor(const NotifySensorArgs* args) override;
- virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ void notifyKey(const NotifyKeyArgs* args) override;
+ void notifyMotion(const NotifyMotionArgs* args) override;
+ void notifySwitch(const NotifySwitchArgs* args) override;
+ void notifySensor(const NotifySensorArgs* args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
- virtual android::os::InputEventInjectionResult injectInputEvent(
+ android::os::InputEventInjectionResult injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
- virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+ std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
- virtual void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
- handlesPerDisplay) override;
- virtual void setFocusedApplication(
+ void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+ handlesPerDisplay) override;
+ void setFocusedApplication(
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
- virtual void setFocusedDisplay(int32_t displayId) override;
- virtual void setInputDispatchMode(bool enabled, bool frozen) override;
- virtual void setInputFilterEnabled(bool enabled) override;
- virtual void setInTouchMode(bool inTouchMode) override;
- virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
- virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
+ void setFocusedDisplay(int32_t displayId) override;
+ void setInputDispatchMode(bool enabled, bool frozen) override;
+ void setInputFilterEnabled(bool enabled) override;
+ void setInTouchMode(bool inTouchMode) override;
+ void setMaximumObscuringOpacityForTouch(float opacity) override;
+ void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop = false) override;
+ bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
+ bool transferTouch(const sp<IBinder>& destChannelToken) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
- virtual void setFocusedWindow(const FocusRequest&) override;
- virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
- bool isGestureMonitor,
- const std::string& name,
- int32_t pid) override;
- virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
- virtual status_t pilferPointers(const sp<IBinder>& token) override;
- virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
- virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+ void setFocusedWindow(const FocusRequest&) override;
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) override;
+ status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
+ status_t pilferPointers(const sp<IBinder>& token) override;
+ void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+ bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index b601dfc..7f85e53 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -151,6 +151,14 @@
*/
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) = 0;
+
+ /**
+ * Transfer touch focus to the provided channel, no matter where the current touch is.
+ *
+ * Return true on success, false if there was no on-going touch.
+ */
+ virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
+
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 9687c83..1da125a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1708,7 +1708,13 @@
0 /*expectedFlags*/);
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+using TransferFunction =
+ std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+
+class TransferTouchFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<TransferFunction> {};
+
+TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows
@@ -1729,8 +1735,10 @@
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- // Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ // Transfer touch to the second window
+ TransferFunction f = GetParam();
+ const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1745,7 +1753,7 @@
secondWindow->consumeMotionUp();
}
-TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -1780,7 +1788,9 @@
secondWindow->assertNoEvents();
// Transfer touch focus to the second window
- mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ TransferFunction f = GetParam();
+ bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
// The first window gets cancel and the second gets down and pointer down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
@@ -1807,6 +1817,21 @@
secondWindow->consumeMotionUp();
}
+// For the cases of single pointer touch and two pointers non-split touch, the api's
+// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
+// for the case where there are multiple pointers split across several windows.
+INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
+ ::testing::Values(
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
+ sp<IBinder> destChannelToken) {
+ return dispatcher->transferTouch(destChannelToken);
+ },
+ [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
+ sp<IBinder> to) {
+ return dispatcher->transferTouchFocus(from, to,
+ false /*isDragAndDrop*/);
+ }));
+
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -1877,6 +1902,82 @@
secondWindow->consumeMotionUp();
}
+// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api.
+// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving
+// touch is not supported, so the touch should continue on those windows and the transferred-to
+// window should get nothing.
+TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setFrame(Rect(0, 0, 600, 400));
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+ secondWindow->setFrame(Rect(0, 400, 600, 800));
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+ PointF pointInFirst = {300, 200};
+ PointF pointInSecond = {300, 600};
+
+ // Send down to the first window
+ NotifyMotionArgs firstDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInFirst});
+ mDispatcher->notifyMotion(&firstDownMotionArgs);
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+
+ // Send down to the second window
+ NotifyMotionArgs secondDownMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&secondDownMotionArgs);
+ // The first window gets a move and the second a down
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionDown();
+
+ // Transfer touch focus to the second window
+ const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
+ // The 'transferTouch' call should not succeed, because there are 2 touched windows
+ ASSERT_FALSE(transferred);
+ firstWindow->assertNoEvents();
+ secondWindow->assertNoEvents();
+
+ // The rest of the dispatch should proceed as normal
+ // Send pointer up to the second window
+ NotifyMotionArgs pointerUpMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInFirst, pointInSecond});
+ mDispatcher->notifyMotion(&pointerUpMotionArgs);
+ // The first window gets MOVE and the second gets pointer up
+ firstWindow->consumeMotionMove();
+ secondWindow->consumeMotionUp();
+
+ // Send up event to the first window
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets nothing and the second gets up
+ firstWindow->consumeMotionUp();
+ secondWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =