Break down input device usage by uid
When InputDispatcher notifies that there is an interaction between a
device and UIDs, track the usage of the input device by UID.
Since Dispatcher calls the policy from its own thread, we use an
AtomicQueue to hold the requests until we process the usages again from
the Reader thread.
Bug: 275726706
Test: atest inputflinger_tests
Test: statsd_testdrive
Change-Id: Ic4aae4bce7d3779adf23309973b964461bd92e7e
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 397b1cd..f92c4f4 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -27,6 +27,7 @@
using android::base::StringPrintf;
using std::chrono::nanoseconds;
+using std::chrono_literals::operator""ns;
namespace {
@@ -72,10 +73,19 @@
ftl::enum_string(src).c_str(), durMillis);
}
+ ALOGD_IF(DEBUG, " Uid breakdown:");
+
+ std::vector<int32_t> uids;
+ std::vector<int32_t> durationsPerUid;
+ for (auto& [uid, dur] : report.uidBreakdown) {
+ uids.push_back(uid);
+ int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
+ durationsPerUid.push_back(durMillis);
+ ALOGD_IF(DEBUG, " - uid: %d\t duration: %dms", uid, durMillis);
+ }
util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
- durationMillis, sources, durationsPerSource, /*uids=*/empty,
- /*usage_durations_per_uid=*/empty);
+ durationMillis, sources, durationsPerSource, uids, durationsPerUid);
}
} sStatsdLogger;
@@ -248,7 +258,11 @@
void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<int32_t>& uids) {
- // TODO: Implement.
+ std::set<Uid> typeSafeUids;
+ for (auto uid : uids) {
+ typeSafeUids.emplace(uid);
+ }
+ mInteractionsQueue.push(DeviceId{deviceId}, timestamp, typeSafeUids);
}
void InputDeviceMetricsCollector::dump(std::string& dump) {
@@ -309,17 +323,33 @@
}
}
-void InputDeviceMetricsCollector::reportCompletedSessions() {
- const auto currentTime = mLogger.getCurrentTime();
+void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
+ auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
+ if (activeSessionIt == mActiveUsageSessions.end()) {
+ return;
+ }
+ activeSessionIt->second.recordInteraction(interaction);
+}
+
+void InputDeviceMetricsCollector::reportCompletedSessions() {
+ // Process all pending interactions.
+ for (auto interaction = mInteractionsQueue.pop(); interaction;
+ interaction = mInteractionsQueue.pop()) {
+ onInputDeviceInteraction(*interaction);
+ }
+
+ const auto currentTime = mLogger.getCurrentTime();
std::vector<DeviceId> completedUsageSessions;
+ // Process usages for all active session to determine if any sessions have expired.
for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
if (activeSession.checkIfCompletedAt(currentTime)) {
completedUsageSessions.emplace_back(deviceId);
}
}
+ // Close out and log all expired usage sessions.
for (DeviceId deviceId : completedUsageSessions) {
const auto infoIt = mLoggedDeviceInfos.find(deviceId);
LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
@@ -351,6 +381,23 @@
mDeviceSession.end = eventTime;
}
+void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
+ const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
+ const auto timestamp = std::get<nanoseconds>(interaction);
+ if (timestamp >= sessionExpiryTime) {
+ // This interaction occurred after the device's current active session is set to expire.
+ // Ignore it.
+ return;
+ }
+
+ for (Uid uid : std::get<std::set<Uid>>(interaction)) {
+ auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
+ if (!inserted) {
+ activeUidIt->second.end = timestamp;
+ }
+ }
+}
+
bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
@@ -365,6 +412,21 @@
mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
mActiveSessionsBySource.erase(it);
}
+
+ std::vector<Uid> completedUidSessionsForDevice;
+ for (auto& [uid, session] : mActiveSessionsByUid) {
+ if (session.end <= sessionExpiryTime) {
+ completedUidSessionsForDevice.emplace_back(uid);
+ }
+ }
+ for (Uid uid : completedUidSessionsForDevice) {
+ auto it = mActiveSessionsByUid.find(uid);
+ const auto& [_, session] = *it;
+ mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
+ mActiveSessionsByUid.erase(it);
+ }
+
+ // This active session has expired if there are no more active source sessions tracked.
return mActiveSessionsBySource.empty();
}
@@ -377,7 +439,12 @@
}
mActiveSessionsBySource.clear();
- return {deviceUsageDuration, mSourceUsageBreakdown};
+ for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
+ mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
+ }
+ mActiveSessionsByUid.clear();
+
+ return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
}
} // namespace android