Add onPointerDownOutsideFocus for events outside the focused window (2/4)
Post the onPointerDownOutsideFocus command when the conditions are satisfied.
Added onPointerDownOutsideFocus to the InputDispatcherPolicyInterface so the
callback can be sent to the client.
Bug: 122535136
Test: InputDispatcherOnPointerDownOutsideFocus
Change-Id: Ifec75daf1fff4ef83e0c230829400abb5a065d7f
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 0d5bc15..4dc9111 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -2055,7 +2055,7 @@
return; // skip the inconsistent event
}
- dispatchPointerDownOutsideFocusIfNecessary(motionEntry->source,
+ dispatchPointerDownOutsideFocus(motionEntry->source,
dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
break;
@@ -2073,10 +2073,11 @@
}
-void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
- if (source != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+ uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
+ if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
return;
}
@@ -2095,7 +2096,9 @@
return;
}
- // Dispatch onPointerDownOutsideFocus to the policy.
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+ commandEntry->newToken = newToken;
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3997,6 +4000,12 @@
entry->release();
}
+void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+ mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
+ mLock.lock();
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 3735a0b..32bc414 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -272,6 +272,13 @@
*/
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid) = 0;
+
+ /* Notifies the policy that a pointer down event has occurred outside the current focused
+ * window.
+ *
+ * The touchedToken passed as an argument is the window that received the input event.
+ */
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
};
@@ -1149,7 +1156,7 @@
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
// The action sent should only be of type AMOTION_EVENT_*
- void dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+ void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) REQUIRES(mLock);
void synthesizeCancelationEventsForAllConnectionsLocked(
@@ -1204,6 +1211,8 @@
DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+ void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
// Statistics gathering.
void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 745fac0..f0c36e5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -52,6 +52,7 @@
mTime = -1;
mAction = -1;
mDisplayId = -1;
+ mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
@@ -87,11 +88,18 @@
<< "Expected filterInputEvent() to not have been called.";
}
+ void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+ ASSERT_EQ(mOnPointerDownToken, touchedToken)
+ << "Expected token from onPointerDownOutsideFocus was not matched";
+ reset();
+ }
+
private:
bool mInputEventFiltered;
nsecs_t mTime;
int32_t mAction;
int32_t mDisplayId;
+ sp<IBinder> mOnPointerDownToken;
virtual void notifyConfigurationChanged(nsecs_t) {
}
@@ -161,11 +169,16 @@
return false;
}
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+ mOnPointerDownToken = newToken;
+ }
+
void reset() {
mInputEventFiltered = false;
mTime = -1;
mAction = -1;
mDisplayId = -1;
+ mOnPointerDownToken.clear();
}
};
@@ -432,10 +445,10 @@
FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
FakeInputReceiver(dispatcher, name, displayId),
- mFocused(false) {
+ mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
mServerChannel->setToken(new BBinder());
mDispatcher->registerInputChannel(mServerChannel, displayId);
-
+
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
@@ -443,15 +456,15 @@
virtual bool updateInfo() {
mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
mInfo.name = mName;
- mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsFlags = mLayoutParamFlags;
mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frameLeft = 0;
- mInfo.frameTop = 0;
- mInfo.frameRight = WIDTH;
- mInfo.frameBottom = HEIGHT;
+ mInfo.frameLeft = mFrame.left;
+ mInfo.frameTop = mFrame.top;
+ mInfo.frameRight = mFrame.right;
+ mInfo.frameBottom = mFrame.bottom;
mInfo.globalScaleFactor = 1.0;
- mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.addTouchableRegion(mFrame);
mInfo.visible = true;
mInfo.canReceiveKeys = true;
mInfo.hasFocus = mFocused;
@@ -470,6 +483,14 @@
mFocused = true;
}
+ void setFrame(const Rect& frame) {
+ mFrame.set(frame);
+ }
+
+ void setLayoutParamFlags(int32_t flags) {
+ mLayoutParamFlags = flags;
+ }
+
void releaseChannel() {
mServerChannel.clear();
InputWindowHandle::releaseChannel();
@@ -480,6 +501,8 @@
}
bool mFocused;
+ Rect mFrame;
+ int32_t mLayoutParamFlags;
};
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -500,7 +523,7 @@
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId) {
+ int32_t displayId, int32_t x = 100, int32_t y = 200) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -510,13 +533,13 @@
pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
event.initialize(DEVICE_ID, source, displayId,
- AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+ AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
/* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
/* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
@@ -907,4 +930,98 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
+ virtual void SetUp() {
+ InputDispatcherTest::SetUp();
+
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+ mWindowFocused->setFrame(Rect(50, 50, 100, 100));
+ mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mWindowFocusedTouchPoint = 60;
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mWindowFocused->setFocus();
+
+ // Expect one focus window exist in display.
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(mUnfocusedWindow);
+ inputWindowHandles.push_back(mWindowFocused);
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ }
+
+ virtual void TearDown() {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mWindowFocused.clear();
+ }
+
+protected:
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mWindowFocused;
+ int32_t mWindowFocusedTouchPoint;
+};
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
+// the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TRACKBALL and action
+// DOWN on the window that doesn't have focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
+// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that already has focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus,
+ OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
+ mWindowFocusedTouchPoint))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
} // namespace android