Skip to content

Commit 3a96aed

Browse files
InputReader: prevent merging pointer sub-devices
If input devices have the same identifier, they are usually merged, on the assumption that they are separate sub-devices that likely got split out by the kernel. This stops the merging of multiple pointer-like input devices, as InputReader is not designed to cope with these properly. This situation is commonly seen with integrated touchscreen/stylus ICs that report touchscreen and stylus input events via distinct HID collections/reports on a single HID device. Bug: 389689566 Test: Target booted, confirmed unmerged devices in InputReaderState via dumpsys inputflinger Test: atest inputflinger_tests # on target, with flag enabled & disabled Test: TEST=inputflinger_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Flag: com.android.input.flags.prevent_merging_input_pointer_devices Change-Id: Iac36f29ed5aa1bf915bf9ad665b237378e967a4f
1 parent 8e94d30 commit 3a96aed

6 files changed

Lines changed: 114 additions & 12 deletions

File tree

libs/input/input_flags.aconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,12 @@ flag {
255255
bug: "277261245"
256256
}
257257

258+
flag {
259+
name: "prevent_merging_input_pointer_devices"
260+
namespace: "desktop_input"
261+
description: "Prevent merging input sub-devices that provide pointer input streams"
262+
bug: "389689566"
263+
metadata {
264+
purpose: PURPOSE_BUGFIX
265+
}
266+
}

services/inputflinger/reader/InputReader.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,28 @@ bool isSubDevice(const InputDeviceIdentifier& identifier1,
6262
identifier1.location == identifier2.location);
6363
}
6464

65+
/**
66+
* Determines if the device classes passed for two devices represent incompatible combinations
67+
* that should not be merged into into a single InputDevice.
68+
*/
69+
70+
bool isCompatibleSubDevice(ftl::Flags<InputDeviceClass> classes1,
71+
ftl::Flags<InputDeviceClass> classes2) {
72+
if (!com::android::input::flags::prevent_merging_input_pointer_devices()) {
73+
return true;
74+
}
75+
76+
const ftl::Flags<InputDeviceClass> pointerFlags =
77+
ftl::Flags<InputDeviceClass>{InputDeviceClass::TOUCH, InputDeviceClass::TOUCH_MT,
78+
InputDeviceClass::CURSOR, InputDeviceClass::TOUCHPAD};
79+
80+
// Do not merge devices that both have any type of pointer event.
81+
if (classes1.any(pointerFlags) && classes2.any(pointerFlags)) return false;
82+
83+
// Safe to merge
84+
return true;
85+
}
86+
6587
bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
6688
const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
6789
if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
@@ -271,7 +293,8 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
271293
}
272294

273295
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
274-
std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier);
296+
ftl::Flags<InputDeviceClass> classes = mEventHub->getDeviceClasses(eventHubId);
297+
std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier, classes);
275298

276299
mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
277300
mPendingArgs += device->reset(when);
@@ -354,12 +377,16 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
354377
}
355378

356379
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
357-
nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) {
358-
auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
359-
const InputDeviceIdentifier identifier2 =
360-
devicePair.second->getDeviceInfo().getIdentifier();
361-
return isSubDevice(identifier, identifier2);
362-
});
380+
nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
381+
ftl::Flags<InputDeviceClass> classes) {
382+
auto deviceIt =
383+
std::find_if(mDevices.begin(), mDevices.end(), [identifier, classes](auto& devicePair) {
384+
const InputDeviceIdentifier identifier2 =
385+
devicePair.second->getDeviceInfo().getIdentifier();
386+
const ftl::Flags<InputDeviceClass> classes2 = devicePair.second->getClasses();
387+
return isSubDevice(identifier, identifier2) &&
388+
isCompatibleSubDevice(classes, classes2);
389+
});
363390

364391
std::shared_ptr<InputDevice> device;
365392
if (deviceIt != mDevices.end()) {

services/inputflinger/reader/include/InputReader.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <vector>
2626

2727
#include "EventHub.h"
28+
#include "InputDevice.h"
2829
#include "InputListener.h"
2930
#include "InputReaderBase.h"
3031
#include "InputReaderContext.h"
@@ -127,7 +128,8 @@ class InputReader : public InputReaderInterface {
127128
protected:
128129
// These members are protected so they can be instrumented by test cases.
129130
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
130-
const InputDeviceIdentifier& identifier)
131+
const InputDeviceIdentifier& identifier,
132+
ftl::Flags<InputDeviceClass> classes)
131133
REQUIRES(mLock);
132134

133135
// With each iteration of the loop, InputReader reads and processes one incoming message from

services/inputflinger/tests/InputReader_test.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,68 @@ TEST_F(InputReaderTest, SetPowerWakeUp) {
14051405
ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(3), false);
14061406
}
14071407

1408+
TEST_F(InputReaderTest, MergeableInputDevices) {
1409+
constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
1410+
1411+
// By default, all of the default-created eventhub devices will have the same identifier
1412+
// (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
1413+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::KEYBOARD, nullptr));
1414+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::JOYSTICK, nullptr));
1415+
1416+
// The two devices will be merged to one input device as they have same identifier, and none are
1417+
// pointer devices.
1418+
ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
1419+
}
1420+
1421+
TEST_F(InputReaderTest, MergeableDevicesWithTouch) {
1422+
constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
1423+
1424+
// By default, all of the default-created eventhub devices will have the same identifier
1425+
// (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
1426+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH_MT, nullptr));
1427+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::KEYBOARD, nullptr));
1428+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::GAMEPAD, nullptr));
1429+
1430+
// The three devices will be merged to one input device as they have same identifier, and only
1431+
// one is a pointer device.
1432+
ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
1433+
}
1434+
1435+
TEST_F(InputReaderTest, UnmergeableTouchDevices) {
1436+
SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
1437+
1438+
constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
1439+
1440+
// By default, all of the default-created eventhub devices will have the same identifier
1441+
// (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
1442+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
1443+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
1444+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "2nd", InputDeviceClass::CURSOR, nullptr));
1445+
1446+
// The three devices will not be merged, as they have same identifier, but are all pointer
1447+
// devices.
1448+
ASSERT_EQ(3U, mFakePolicy->getInputDevices().size());
1449+
}
1450+
1451+
TEST_F(InputReaderTest, MergeableMixedDevices) {
1452+
SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
1453+
1454+
constexpr int32_t eventHubIds[4] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2,
1455+
END_RESERVED_ID + 3};
1456+
1457+
// By default, all of the default-created eventhub devices will have the same identifier
1458+
// (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
1459+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
1460+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
1461+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::DPAD, nullptr));
1462+
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[3], "4th", InputDeviceClass::JOYSTICK, nullptr));
1463+
1464+
// Non-touch devices can be merged with one of the touch devices, as they have same identifier,
1465+
// but the two touch devices will not combine with each other. It is not specified which touch
1466+
// device the non-touch devices merge with.
1467+
ASSERT_EQ(2U, mFakePolicy->getInputDevices().size());
1468+
}
1469+
14081470
// --- InputReaderIntegrationTest ---
14091471

14101472
// These tests create and interact with the InputReader only through its interface.

services/inputflinger/tests/InstrumentedInputReader.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId
3838
}
3939

4040
std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
41-
nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
41+
nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
42+
ftl::Flags<InputDeviceClass> classes) REQUIRES(mLock) {
4243
if (!mNextDevices.empty()) {
4344
std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
4445
mNextDevices.pop();
4546
return device;
4647
}
47-
return InputReader::createDeviceLocked(when, eventHubId, identifier);
48+
return InputReader::createDeviceLocked(when, eventHubId, identifier, classes);
4849
}
4950

5051
} // namespace android

services/inputflinger/tests/InstrumentedInputReader.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ class InstrumentedInputReader : public InputReader {
4343
using InputReader::loopOnce;
4444

4545
protected:
46-
virtual std::shared_ptr<InputDevice> createDeviceLocked(
47-
nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier);
46+
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t eventHubId,
47+
const InputDeviceIdentifier& identifier,
48+
ftl::Flags<InputDeviceClass> classes);
4849

4950
class FakeInputReaderContext : public ContextImpl {
5051
public:

0 commit comments

Comments
 (0)