Skip to content

Commit a86a633

Browse files
committed
Introduce AccessibilityPointerMotionFilter
This new input event filter is used to filter relative motion events from pointer cursor devices. This allows the filter to consume some amount of the relative motion events before the curosor location in the screen is determined by PointerChoreographer. Accessibility fullscreen magnification will use this filter. Bug: 379646448 Bug: 361817142 Test: PointerChoreographerTest Flag: com.android.server.accessibility.enable_magnification_follows_mouse_with_pointer_motion_filter Change-Id: I8d5d6bc1fc6b43d08c34906a8fbe843a598d5d16
1 parent 9af0a06 commit a86a633

5 files changed

Lines changed: 135 additions & 2 deletions

File tree

services/inputflinger/PointerChoreographer.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <android-base/logging.h>
2020
#include <android/configuration.h>
2121
#include <com_android_input_flags.h>
22+
#include <algorithm>
2223
#if defined(__ANDROID__)
2324
#include <gui/SurfaceComposerClient.h>
2425
#endif
@@ -165,6 +166,7 @@ PointerChoreographer::PointerChoreographer(
165166
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
166167
mShowTouchesEnabled(false),
167168
mStylusPointerIconEnabled(false),
169+
mPointerMotionFilterEnabled(false),
168170
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
169171
mIsWindowInfoListenerRegistered(false),
170172
mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -322,8 +324,10 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg
322324
PointerControllerInterface& pc) {
323325
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
324326
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
325-
326-
vec2 unconsumedDelta = pc.move(deltaX, deltaY);
327+
vec2 filteredDelta =
328+
filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
329+
newArgs.displayId);
330+
vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
327331
if (com::android::input::flags::connected_displays_cursor() &&
328332
(std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
329333
handleUnconsumedDeltaLocked(pc, unconsumedDelta);
@@ -638,6 +642,8 @@ void PointerChoreographer::dump(std::string& dump) {
638642
mShowTouchesEnabled ? "true" : "false");
639643
dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
640644
mStylusPointerIconEnabled ? "true" : "false");
645+
dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
646+
mPointerMotionFilterEnabled ? "true" : "false");
641647

642648
dump += INDENT "MousePointerControllers:\n";
643649
for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
@@ -973,6 +979,11 @@ void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
973979
mCurrentFocusedDisplay = displayId;
974980
}
975981

982+
void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
983+
std::scoped_lock _l(getLock());
984+
mPointerMotionFilterEnabled = enabled;
985+
}
986+
976987
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
977988
ui::LogicalDisplayId displayId) {
978989
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -1046,6 +1057,21 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so
10461057
return std::nullopt;
10471058
}
10481059

1060+
vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
1061+
const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
1062+
if (!mPointerMotionFilterEnabled) {
1063+
return delta;
1064+
}
1065+
std::optional<vec2> filterResult =
1066+
mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
1067+
if (!filterResult.has_value()) {
1068+
// Disable filter when there's any error.
1069+
mPointerMotionFilterEnabled = false;
1070+
return delta;
1071+
}
1072+
return *filterResult;
1073+
}
1074+
10491075
// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
10501076

10511077
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(

services/inputflinger/PointerChoreographer.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ class PointerChoreographerInterface : public InputListenerInterface {
9090
* This method may be called on any thread (usually by the input manager on a binder thread).
9191
*/
9292
virtual void dump(std::string& dump) = 0;
93+
94+
/**
95+
* Enables motion event filter before pointer coordinates are determined.
96+
*/
97+
virtual void setAccessibilityPointerMotionFilterEnabled(bool enabled) = 0;
9398
};
9499

95100
class PointerChoreographer : public PointerChoreographerInterface {
@@ -110,6 +115,7 @@ class PointerChoreographer : public PointerChoreographerInterface {
110115
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
111116
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
112117
void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
118+
void setAccessibilityPointerMotionFilterEnabled(bool enabled) override;
113119

114120
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
115121
void notifyKey(const NotifyKeyArgs& args) override;
@@ -168,6 +174,10 @@ class PointerChoreographer : public PointerChoreographerInterface {
168174
const DisplayTopologyPosition sourceBoundary,
169175
int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
170176

177+
vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta,
178+
const ui::LogicalDisplayId& displayId)
179+
REQUIRES(getLock());
180+
171181
/* Topology is initialized with default-constructed value, which is an empty topology. Till we
172182
* receive setDisplayTopology call.
173183
* Meanwhile Choreographer will treat every display as independent disconnected display.
@@ -228,6 +238,7 @@ class PointerChoreographer : public PointerChoreographerInterface {
228238
std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
229239
bool mShowTouchesEnabled GUARDED_BY(getLock());
230240
bool mStylusPointerIconEnabled GUARDED_BY(getLock());
241+
bool mPointerMotionFilterEnabled GUARDED_BY(getLock());
231242
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
232243
ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
233244

services/inputflinger/include/PointerChoreographerPolicyInterface.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ class PointerChoreographerPolicyInterface {
6161

6262
/* Notifies that mouse cursor faded due to typing. */
6363
virtual void notifyMouseCursorFadedOnTyping() = 0;
64+
65+
/**
66+
* Give accessibility a chance to filter motion event by pointer devices.
67+
* The return values denotes the delta x and y after filtering it.
68+
*
69+
* This call happens on the input hot path and it is extremely performance sensitive.
70+
* This also must not call back into native code.
71+
*/
72+
virtual std::optional<vec2> filterPointerMotionForAccessibility(
73+
const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) = 0;
6474
};
6575

6676
} // namespace android

services/inputflinger/tests/InterfaceMocks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolic
191191
(ui::LogicalDisplayId displayId, const vec2& position), (override));
192192
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
193193
MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
194+
MOCK_METHOD(std::optional<vec2>, filterPointerMotionForAccessibility,
195+
(const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId),
196+
(override));
194197
};
195198

196199
class MockInputDevice : public InputDevice {

services/inputflinger/tests/PointerChoreographer_test.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,89 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) {
17761776
firstMousePc->assertPointerIconNotSet();
17771777
}
17781778

1779+
TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) {
1780+
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
1781+
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
1782+
mChoreographer.notifyInputDevicesChanged(
1783+
{/*id=*/0,
1784+
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
1785+
ui::LogicalDisplayId::INVALID)}});
1786+
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
1787+
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
1788+
1789+
pc->setPosition(100, 200);
1790+
mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
1791+
1792+
EXPECT_CALL(mMockPolicy,
1793+
filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
1794+
testing::Eq(vec2{10.f, 20.f}),
1795+
testing::Eq(DISPLAY_ID)))
1796+
.Times(1)
1797+
.WillOnce(testing::Return(vec2{4, 13}));
1798+
1799+
mChoreographer.notifyMotion(
1800+
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
1801+
.pointer(MOUSE_POINTER)
1802+
.deviceId(DEVICE_ID)
1803+
.displayId(ui::LogicalDisplayId::INVALID)
1804+
.build());
1805+
1806+
// Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
1807+
pc->assertPosition(104, 213);
1808+
mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
1809+
WithCursorPosition(104, 213),
1810+
WithRelativeMotion(10, 20)));
1811+
}
1812+
1813+
TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) {
1814+
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
1815+
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
1816+
mChoreographer.notifyInputDevicesChanged(
1817+
{/*id=*/0,
1818+
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
1819+
ui::LogicalDisplayId::INVALID)}});
1820+
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
1821+
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
1822+
1823+
pc->setPosition(100, 200);
1824+
mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
1825+
1826+
EXPECT_CALL(mMockPolicy,
1827+
filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
1828+
testing::Eq(vec2{10.f, 20.f}),
1829+
testing::Eq(DISPLAY_ID)))
1830+
.Times(1)
1831+
.WillOnce(testing::Return(vec2{4, 13}));
1832+
1833+
mChoreographer.notifyMotion(
1834+
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
1835+
.pointer(TOUCHPAD_POINTER)
1836+
.deviceId(DEVICE_ID)
1837+
.displayId(ui::LogicalDisplayId::INVALID)
1838+
.build());
1839+
1840+
// Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
1841+
pc->assertPosition(104, 213);
1842+
mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
1843+
WithCursorPosition(104, 213),
1844+
WithRelativeMotion(10, 20)));
1845+
}
1846+
1847+
TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) {
1848+
mChoreographer.notifyInputDevicesChanged(
1849+
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
1850+
mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
1851+
1852+
EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility).Times(0);
1853+
1854+
mChoreographer.notifyMotion(
1855+
MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
1856+
.pointer(FIRST_TOUCH_POINTER)
1857+
.deviceId(DEVICE_ID)
1858+
.displayId(DISPLAY_ID)
1859+
.build());
1860+
}
1861+
17791862
using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
17801863
std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
17811864
std::function<void(PointerChoreographer&)>, int32_t /*action*/>;

0 commit comments

Comments
 (0)