Pass ProductId+VendorId+Source info to LatencyTracker
Currently InputEventLatencySketch captures the aggregated latency over
all devices, and it doesn't capture per-device latency.
In order to capture latency metrics per device, we track the timeline
for latency information for each Product Id, Vendor Id and Source
individually.
Bug: 270049345
Test: atest inputflinger_tests:LatencyTrackerTest
Change-Id: Ic3265e25ff95266e8cfe8542d4f4afb7b6ac16b1
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
index 3ac91c8..a6be8f4 100644
--- a/services/inputflinger/InputDeviceMetricsSource.h
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -47,6 +47,8 @@
ftl_first = UNKNOWN,
ftl_last = TRACKBALL,
+ // Used by latency fuzzer
+ kMaxValue = ftl_last
};
/** Returns the InputDeviceUsageSource that corresponds to the key event. */
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6226a19..16ce1e4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -46,6 +46,8 @@
#include <queue>
#include <sstream>
+#include "../InputDeviceMetricsSource.h"
+
#include "Connection.h"
#include "DebugConfig.h"
#include "InputDispatcher.h"
@@ -4183,6 +4185,11 @@
return splitMotionEntry;
}
+void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ std::scoped_lock _l(mLock);
+ mLatencyTracker.setInputDevices(args.inputDeviceInfos);
+}
+
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
if (debugInboundEventDetails()) {
ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
@@ -4395,7 +4402,9 @@
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
- mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);
+ std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
+ mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime,
+ args.deviceId, sources);
}
needWake = enqueueInboundEventLocked(std::move(newEntry));
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 62e2d58..ee5a797 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -92,7 +92,7 @@
status_t start() override;
status_t stop() override;
- void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override{};
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index 3edb638..a7c6d16 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -16,6 +16,8 @@
#include "InputEventTimeline.h"
+#include "../InputDeviceMetricsSource.h"
+
namespace android::inputdispatcher {
ConnectionTimeline::ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime,
@@ -64,8 +66,15 @@
return !operator==(rhs);
}
-InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime)
- : isDown(isDown), eventTime(eventTime), readTime(readTime) {}
+InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
+ uint16_t vendorId, uint16_t productId,
+ std::set<InputDeviceUsageSource> sources)
+ : isDown(isDown),
+ eventTime(eventTime),
+ readTime(readTime),
+ vendorId(vendorId),
+ productId(productId),
+ sources(sources) {}
bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const {
if (connectionTimelines.size() != rhs.connectionTimelines.size()) {
@@ -80,7 +89,8 @@
return false;
}
}
- return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime;
+ return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index daf375d..e9deb2d 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -16,6 +16,8 @@
#pragma once
+#include "../InputDeviceMetricsSource.h"
+
#include <binder/IBinder.h>
#include <input/Input.h>
#include <unordered_map>
@@ -73,10 +75,14 @@
};
struct InputEventTimeline {
- InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
+ uint16_t productId, std::set<InputDeviceUsageSource> sources);
const bool isDown; // True if this is an ACTION_DOWN event
const nsecs_t eventTime;
const nsecs_t readTime;
+ const uint16_t vendorId;
+ const uint16_t productId;
+ const std::set<InputDeviceUsageSource> sources;
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index b7c36a8..698bd9f 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "LatencyTracker"
#include "LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
#include <inttypes.h>
@@ -23,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <log/log.h>
using android::base::HwTimeoutMultiplier;
@@ -66,7 +68,8 @@
}
void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime,
- nsecs_t readTime) {
+ nsecs_t readTime, DeviceId deviceId,
+ const std::set<InputDeviceUsageSource>& sources) {
reportAndPruneMatureRecords(eventTime);
const auto it = mTimelines.find(inputEventId);
if (it != mTimelines.end()) {
@@ -78,7 +81,29 @@
eraseByValue(mEventTimes, inputEventId);
return;
}
- mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
+
+ // Create an InputEventTimeline for the device ID. The vendorId and productId
+ // can be obtained from the InputDeviceIdentifier of the particular device.
+ const InputDeviceIdentifier* identifier = nullptr;
+ for (auto& inputDevice : mInputDevices) {
+ if (deviceId == inputDevice.getId()) {
+ identifier = &inputDevice.getIdentifier();
+ break;
+ }
+ }
+
+ // If no matching ids can be found for the device from among the input devices connected,
+ // the call to trackListener will be dropped.
+ // Note: there generally isn't expected to be a situation where we can't find an InputDeviceInfo
+ // but a possibility of it is handled in case of race conditions
+ if (identifier == nullptr) {
+ ALOGE("Could not find input device identifier. Dropping call to LatencyTracker.");
+ return;
+ }
+
+ mTimelines.emplace(inputEventId,
+ InputEventTimeline(isDown, eventTime, readTime, identifier->vendor,
+ identifier->product, sources));
mEventTimes.emplace(eventTime, inputEventId);
}
@@ -171,4 +196,8 @@
StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
}
+void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) {
+ mInputDevices = inputDevices;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 4212da8..890d61d 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -16,6 +16,8 @@
#pragma once
+#include "../InputDeviceMetricsSource.h"
+
#include <map>
#include <unordered_map>
@@ -23,6 +25,7 @@
#include <input/Input.h>
#include "InputEventTimeline.h"
+#include "NotifyArgs.h"
namespace android::inputdispatcher {
@@ -49,13 +52,15 @@
* duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
* must drop all duplicate data.
*/
- void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime,
+ DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
std::string dump(const char* prefix) const;
+ void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
private:
/**
@@ -76,6 +81,7 @@
std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
InputEventTimelineProcessor* mTimelineProcessor;
+ std::vector<InputDeviceInfo> mInputDevices;
void reportAndPruneMatureRecords(nsecs_t newEventTime);
};
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index fa149db..6606de8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -15,11 +15,14 @@
*/
#include "../dispatcher/LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
+#include <gui/constants.h>
#include <inttypes.h>
+#include <linux/input.h>
#include <log/log.h>
#define TAG "LatencyTracker_test"
@@ -30,6 +33,29 @@
namespace android::inputdispatcher {
+namespace {
+
+constexpr DeviceId DEVICE_ID = 100;
+
+static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId,
+ DeviceId deviceId) {
+ InputDeviceIdentifier identifier;
+ identifier.vendor = vendorId;
+ identifier.product = productId;
+ auto info = InputDeviceInfo();
+ info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
+ /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+ return info;
+}
+
+void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
+ InputDeviceInfo deviceInfo = generateTestDeviceInfo(
+ /*vendorId=*/0, /*productId=*/0, DEVICE_ID);
+ tracker.setInputDevices({deviceInfo});
+}
+
+} // namespace
+
const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
HwTimeoutMultiplier());
@@ -38,7 +64,10 @@
InputEventTimeline t(
/*isDown=*/true,
/*eventTime=*/2,
- /*readTime=*/3);
+ /*readTime=*/3,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
@@ -60,6 +89,7 @@
connection2 = sp<BBinder>::make();
mTracker = std::make_unique<LatencyTracker>(this);
+ setDefaultInputDeviceInfo(*mTracker);
}
void TearDown() override {}
@@ -88,7 +118,8 @@
const nsecs_t triggerEventTime =
lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime,
- /*readTime=*/3);
+ /*readTime=*/3, DEVICE_ID,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
}
void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
@@ -138,9 +169,11 @@
*/
TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2,
- /*readTime=*/3);
+ /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(/*eventTime=*/2);
- assertReceivedTimeline(InputEventTimeline{false, 2, 3});
+ assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2,
+ /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN}});
}
/**
@@ -171,7 +204,8 @@
const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+ DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -191,8 +225,10 @@
// In the following 2 calls to trackListener, the inputEventId's are the same, but event times
// are different.
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime);
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime);
+ mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(/*eventTime=*/2);
// Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -205,7 +241,10 @@
InputEventTimeline timeline1(
/*isDown*/ true,
/*eventTime*/ 2,
- /*readTime*/ 3);
+ /*readTime*/ 3,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
timeline1.connectionTimelines.emplace(connection1,
ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
/*finishTime*/ 8));
@@ -219,7 +258,10 @@
InputEventTimeline timeline2(
/*isDown=*/false,
/*eventTime=*/20,
- /*readTime=*/30);
+ /*readTime=*/30,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
timeline2.connectionTimelines.emplace(connection2,
ConnectionTimeline(/*deliveryTime=*/60,
/*consumeTime=*/70,
@@ -232,10 +274,10 @@
// Start processing first event
mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime,
- timeline1.readTime);
+ timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
// Start processing second event
mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime,
- timeline2.readTime);
+ timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
@@ -261,9 +303,11 @@
for (size_t i = 1; i <= 100; i++) {
mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime,
- timeline.readTime);
- expectedTimelines.push_back(
- InputEventTimeline{timeline.isDown, timeline.eventTime, timeline.readTime});
+ timeline.readTime, /*deviceId=*/DEVICE_ID,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime,
+ timeline.readTime, timeline.vendorId,
+ timeline.productId, timeline.sources});
}
// Now, complete the first event that was sent.
mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime,
@@ -289,10 +333,38 @@
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+ DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(expected.eventTime);
- assertReceivedTimeline(
- InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
+ assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime,
+ expected.readTime, expected.vendorId,
+ expected.productId, expected.sources});
+}
+
+/**
+ * Check that LatencyTracker has the received timeline that contains the correctly
+ * resolved product ID, vendor ID and source for a particular device ID from
+ * among a list of devices.
+ */
+TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) {
+ constexpr int32_t inputEventId = 1;
+ InputEventTimeline timeline(
+ /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3,
+ /*vendorId=*/50, /*productId=*/60,
+ /*sources=*/
+ {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT});
+ InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
+ /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
+ InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
+ /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
+
+ mTracker->setInputDevices({deviceInfo1, deviceInfo2});
+ mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime,
+ DEVICE_ID,
+ {InputDeviceUsageSource::TOUCHSCREEN,
+ InputDeviceUsageSource::STYLUS_DIRECT});
+ triggerEventReporting(timeline.eventTime);
+ assertReceivedTimeline(timeline);
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 72780fb..6daeaaf 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -15,6 +15,9 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
+#include <linux/input.h>
+
+#include "../../InputDeviceMetricsSource.h"
#include "dispatcher/LatencyTracker.h"
namespace android {
@@ -65,7 +68,11 @@
int32_t isDown = fdp.ConsumeBool();
nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
- tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+ const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
+ std::set<InputDeviceUsageSource> sources = {
+ fdp.ConsumeEnum<InputDeviceUsageSource>()};
+ tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId,
+ sources);
},
[&]() -> void {
int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();