Support native API to report sysfs nodes changes
When a sysfs node change is reported, check if any new
perpherals are added and if added, recreate input device.
Test: atest inputfliger_tests
Bug: 265057712
Change-Id: Ic6c0c00de9ae963f0651859bdbc57e45af7ccd07
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 841c914..1750c64 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -149,6 +149,9 @@
/* Get the Bluetooth address of an input device, if known. */
virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
+
+ /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
+ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
};
// --- InputReaderConfiguration ---
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e65f3af..ee8dde1 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1531,6 +1531,20 @@
return associatedDevice;
}
+bool EventHub::AssociatedDevice::isChanged() const {
+ std::unordered_map<int32_t, RawBatteryInfo> newBatteryInfos =
+ readBatteryConfiguration(sysfsRootPath);
+ std::unordered_map<int32_t, RawLightInfo> newLightInfos =
+ readLightsConfiguration(sysfsRootPath);
+ std::optional<RawLayoutInfo> newLayoutInfo = readLayoutConfiguration(sysfsRootPath);
+
+ if (newBatteryInfos == batteryInfos && newLightInfos == lightInfos &&
+ newLayoutInfo == layoutInfo) {
+ return false;
+ }
+ return true;
+}
+
void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -2536,6 +2550,42 @@
return device->disable();
}
+// TODO(b/274755573): Shift to uevent handling on native side and remove this method
+// Currently using Java UEventObserver to trigger this which uses UEvent infrastructure that uses a
+// NETLINK socket to observe UEvents. We can create similar infrastructure on Eventhub side to
+// directly observe UEvents instead of triggering from Java side.
+void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
+ std::scoped_lock _l(mLock);
+
+ // Check in opening devices
+ for (auto it = mOpeningDevices.begin(); it != mOpeningDevices.end(); it++) {
+ std::unique_ptr<Device>& device = *it;
+ if (device->associatedDevice &&
+ sysfsNodePath.find(device->associatedDevice->sysfsRootPath.string()) !=
+ std::string::npos &&
+ device->associatedDevice->isChanged()) {
+ it = mOpeningDevices.erase(it);
+ openDeviceLocked(device->path);
+ }
+ }
+
+ // Check in already added device
+ std::vector<Device*> devicesToReopen;
+ for (const auto& [id, device] : mDevices) {
+ if (device->associatedDevice &&
+ sysfsNodePath.find(device->associatedDevice->sysfsRootPath.string()) !=
+ std::string::npos &&
+ device->associatedDevice->isChanged()) {
+ devicesToReopen.push_back(device.get());
+ }
+ }
+ for (const auto& device : devicesToReopen) {
+ closeDeviceLocked(*device);
+ openDeviceLocked(device->path);
+ }
+ devicesToReopen.clear();
+}
+
void EventHub::createVirtualKeyboardLocked() {
InputDeviceIdentifier identifier;
identifier.name = "Virtual";
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 9080cc1..81ac03b 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -928,6 +928,10 @@
return *associatedDisplayId == displayId;
}
+void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) {
+ mEventHub->sysfsNodeChanged(sysfsNodePath);
+}
+
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 0b15efe..20612c7 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -388,6 +388,10 @@
/* Disable an input device. Closes file descriptor to that device. */
virtual status_t disableDevice(int32_t deviceId) = 0;
+
+ /* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery,
+ * etc. is detected. */
+ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
};
template <std::size_t BITS>
@@ -567,6 +571,8 @@
status_t disableDevice(int32_t deviceId) override final;
+ void sysfsNodeChanged(const std::string& sysfsNodePath) override final;
+
~EventHub() override;
private:
@@ -578,6 +584,7 @@
std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
std::optional<RawLayoutInfo> layoutInfo;
+ bool isChanged() const;
bool operator==(const AssociatedDevice&) const = default;
bool operator!=(const AssociatedDevice&) const = default;
std::string dump() const;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index e9c989a..120e150 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -117,6 +117,8 @@
std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+ void sysfsNodeChanged(const std::string& sysfsNodePath) override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index ff6d584..4626f5a 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -594,4 +594,33 @@
return lightIt->second;
};
+void FakeEventHub::setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) const {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ return;
+ }
+ device->sysfsRootPath = sysfsRootPath;
+}
+
+void FakeEventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
+ int32_t foundDeviceId = -1;
+ Device* foundDevice = nullptr;
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ Device* d = mDevices.valueAt(i);
+ if (sysfsNodePath.find(d->sysfsRootPath) != std::string::npos) {
+ foundDeviceId = mDevices.keyAt(i);
+ foundDevice = d;
+ }
+ }
+ if (foundDevice == nullptr) {
+ return;
+ }
+ // If device sysfs changed -> reopen the device
+ if (!mRawLightInfos.empty() && !foundDevice->classes.test(InputDeviceClass::LIGHT)) {
+ removeDevice(foundDeviceId);
+ addDevice(foundDeviceId, foundDevice->identifier.name,
+ foundDevice->classes | InputDeviceClass::LIGHT, foundDevice->identifier.bus);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index e0a3f9e..8e06940 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -64,6 +64,7 @@
std::vector<VirtualKeyDefinition> virtualKeys;
bool enabled;
std::optional<RawLayoutInfo> layoutInfo;
+ std::string sysfsRootPath;
status_t enable() {
enabled = true;
@@ -152,6 +153,7 @@
void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
int32_t value);
void assertQueueIsEmpty();
+ void setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) const;
private:
Device* getDevice(int32_t deviceId) const;
@@ -212,7 +214,7 @@
std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
int32_t deviceId, int32_t lightId) const override;
-
+ void sysfsNodeChanged(const std::string& sysfsNodePath) override;
void dump(std::string&) const override {}
void monitor() const override {}
void requestReopenDevices() override {}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 853c5b0..48ffc15 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -644,6 +644,30 @@
ASSERT_EQ(0U, inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, InputDeviceRecreatedOnSysfsNodeChanged) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+ mFakeEventHub->setSysfsRootPath(1, "xyz");
+
+ // Should also have received a notification describing the new input device.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+ InputDeviceInfo inputDevice = mFakePolicy->getInputDevices()[0];
+ ASSERT_EQ(0U, inputDevice.getLights().size());
+
+ RawLightInfo infoMonolight = {.id = 123,
+ .name = "mono_keyboard_backlight",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(/*rawId=*/123, std::move(infoMonolight));
+ mReader->sysfsNodeChanged("xyz");
+ mReader->loopOnce();
+
+ // Should also have received a notification describing the new recreated input device.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ inputDevice = mFakePolicy->getInputDevices()[0];
+ ASSERT_EQ(1U, inputDevice.getLights().size());
+}
+
TEST_F(InputReaderTest, GetMergedInputDevices) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 20242b1..baece3c 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -165,6 +165,10 @@
return reader->getBluetoothAddress(deviceId);
}
+ void sysfsNodeChanged(const std::string& sysfsNodePath) {
+ reader->sysfsNodeChanged(sysfsNodePath);
+ }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 2cb5cdf..c082128 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -217,6 +217,7 @@
bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); }
status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+ void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
};
class FuzzPointerController : public PointerControllerInterface {