Break down input device usage by source

Bug: 275726706
Test: atest inputflinger_tests
Test: statsd_testdrive
Change-Id: Ic652ecd7c66b9e9c3b46d84454485cf51140a14b
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 71f3717..3e25cc3 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -53,18 +53,28 @@
     nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
 
     void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
-                                     nanoseconds sessionDuration) override {
+                                     const DeviceUsageReport& report) override {
         const int32_t durationMillis =
-                std::chrono::duration_cast<std::chrono::milliseconds>(sessionDuration).count();
+                std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
         const static std::vector<int32_t> empty;
 
         ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
         ALOGD_IF(DEBUG, "    Total duration: %dms", durationMillis);
+        ALOGD_IF(DEBUG, "    Source breakdown:");
+
+        std::vector<int32_t> sources;
+        std::vector<int32_t> durationsPerSource;
+        for (auto& [src, dur] : report.sourceBreakdown) {
+            sources.push_back(ftl::to_underlying(src));
+            int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
+            durationsPerSource.emplace_back(durMillis);
+            ALOGD_IF(DEBUG, "        - usageSource: %s\t duration: %dms",
+                     ftl::enum_string(src).c_str(), durMillis);
+        }
 
         util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
                           identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
-                          durationMillis, /*usage_sources=*/empty,
-                          /*usage_durations_per_source=*/empty, /*uids=*/empty,
+                          durationMillis, sources, durationsPerSource, /*uids=*/empty,
                           /*usage_durations_per_uid=*/empty);
     }
 } sStatsdLogger;
@@ -181,54 +191,58 @@
 
 void InputDeviceMetricsCollector::notifyInputDevicesChanged(
         const NotifyInputDevicesChangedArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     onInputDevicesChanged(args.inputDeviceInfos);
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyConfigurationChanged(
         const NotifyConfigurationChangedArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
-    processUsages();
-    onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime));
+    reportCompletedSessions();
+    const SourceProvider getSources = [&args](const InputDeviceInfo& info) {
+        return std::set{getUsageSourceForKeyArgs(info, args)};
+    };
+    onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
 
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
-    processUsages();
-    onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime));
+    reportCompletedSessions();
+    onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
+                       [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
 
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
 void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
         const NotifyPointerCaptureChangedArgs& args) {
-    processUsages();
+    reportCompletedSessions();
     mNextListener.notify(args);
 }
 
@@ -241,69 +255,124 @@
 }
 
 void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
-    std::map<DeviceId, InputDeviceIdentifier> newDeviceIds;
+    std::map<DeviceId, InputDeviceInfo> newDeviceInfos;
 
     for (const InputDeviceInfo& info : infos) {
         if (isIgnoredInputDeviceId(info.getId())) {
             continue;
         }
-        newDeviceIds.emplace(info.getId(), info.getIdentifier());
+        newDeviceInfos.emplace(info.getId(), info);
     }
 
-    for (auto [deviceId, identifier] : mLoggedDeviceInfos) {
-        if (newDeviceIds.count(deviceId) != 0) {
+    for (auto [deviceId, info] : mLoggedDeviceInfos) {
+        if (newDeviceInfos.count(deviceId) != 0) {
             continue;
         }
-        onInputDeviceRemoved(deviceId, identifier);
+        onInputDeviceRemoved(deviceId, info.getIdentifier());
     }
 
-    std::swap(newDeviceIds, mLoggedDeviceInfos);
+    std::swap(newDeviceInfos, mLoggedDeviceInfos);
 }
 
 void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
                                                        const InputDeviceIdentifier& identifier) {
-    // Report usage for that device if there is an active session.
     auto it = mActiveUsageSessions.find(deviceId);
-    if (it != mActiveUsageSessions.end()) {
-        mLogger.logInputDeviceUsageReported(identifier, it->second.end - it->second.start);
-        mActiveUsageSessions.erase(it);
+    if (it == mActiveUsageSessions.end()) {
+        return;
     }
+    // Report usage for that device if there is an active session.
+    auto& [_, activeSession] = *it;
+    mLogger.logInputDeviceUsageReported(identifier, activeSession.finishSession());
+    mActiveUsageSessions.erase(it);
+
     // We don't remove this from mLoggedDeviceInfos because it will be updated in
     // onInputDevicesChanged().
 }
 
-void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime) {
-    if (mLoggedDeviceInfos.count(deviceId) == 0) {
+void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
+                                                     const SourceProvider& getSources) {
+    auto infoIt = mLoggedDeviceInfos.find(deviceId);
+    if (infoIt == mLoggedDeviceInfos.end()) {
         // Do not track usage for devices that are not logged.
         return;
     }
 
-    auto [it, inserted] = mActiveUsageSessions.try_emplace(deviceId, eventTime, eventTime);
-    if (!inserted) {
-        it->second.end = eventTime;
+    auto [sessionIt, _] =
+            mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
+    for (InputDeviceUsageSource source : getSources(infoIt->second)) {
+        sessionIt->second.recordUsage(eventTime, source);
     }
 }
 
-void InputDeviceMetricsCollector::processUsages() {
-    const auto usageSessionExpiryTime = mLogger.getCurrentTime() - mUsageSessionTimeout;
+void InputDeviceMetricsCollector::reportCompletedSessions() {
+    const auto currentTime = mLogger.getCurrentTime();
 
     std::vector<DeviceId> completedUsageSessions;
 
-    for (const auto& [deviceId, usageSession] : mActiveUsageSessions) {
-        if (usageSession.end <= usageSessionExpiryTime) {
+    for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
+        if (activeSession.checkIfCompletedAt(currentTime)) {
             completedUsageSessions.emplace_back(deviceId);
         }
     }
 
     for (DeviceId deviceId : completedUsageSessions) {
-        const auto it = mLoggedDeviceInfos.find(deviceId);
-        LOG_ALWAYS_FATAL_IF(it == mLoggedDeviceInfos.end());
+        const auto infoIt = mLoggedDeviceInfos.find(deviceId);
+        LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
 
-        const auto& session = mActiveUsageSessions[deviceId];
-        mLogger.logInputDeviceUsageReported(it->second, session.end - session.start);
-
-        mActiveUsageSessions.erase(deviceId);
+        auto activeSessionIt = mActiveUsageSessions.find(deviceId);
+        LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
+        auto& [_, activeSession] = *activeSessionIt;
+        mLogger.logInputDeviceUsageReported(infoIt->second.getIdentifier(),
+                                            activeSession.finishSession());
+        mActiveUsageSessions.erase(activeSessionIt);
     }
 }
 
+// --- InputDeviceMetricsCollector::ActiveSession ---
+
+InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
+                                                          nanoseconds startTime)
+      : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
+
+void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
+                                                             InputDeviceUsageSource source) {
+    // We assume that event times for subsequent events are always monotonically increasing for each
+    // input device.
+    auto [activeSourceIt, inserted] =
+            mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
+    if (!inserted) {
+        activeSourceIt->second.end = eventTime;
+    }
+    mDeviceSession.end = eventTime;
+}
+
+bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
+    const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
+    std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
+    for (auto& [source, session] : mActiveSessionsBySource) {
+        if (session.end <= sessionExpiryTime) {
+            completedSourceSessionsForDevice.emplace_back(source);
+        }
+    }
+    for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
+        auto it = mActiveSessionsBySource.find(source);
+        const auto& [_, session] = *it;
+        mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
+        mActiveSessionsBySource.erase(it);
+    }
+    return mActiveSessionsBySource.empty();
+}
+
+InputDeviceMetricsLogger::DeviceUsageReport
+InputDeviceMetricsCollector::ActiveSession::finishSession() {
+    const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
+
+    for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
+        mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
+    }
+    mActiveSessionsBySource.clear();
+
+    return {deviceUsageDuration, mSourceUsageBreakdown};
+}
+
 } // namespace android