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.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 4ef860f..e2e79e4 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -23,6 +23,7 @@
 #include <input/InputDevice.h>
 #include <statslog.h>
 #include <chrono>
+#include <functional>
 #include <map>
 #include <set>
 #include <vector>
@@ -79,8 +80,25 @@
 class InputDeviceMetricsLogger {
 public:
     virtual std::chrono::nanoseconds getCurrentTime() = 0;
+
+    // Describes the breakdown of an input device usage session by its usage sources.
+    // An input device can have more than one usage source. For example, some game controllers have
+    // buttons, joysticks, and touchpads. We track usage by these sources to get a better picture of
+    // the device usage. The source breakdown of a 10 minute usage session could look like this:
+    //   { {GAMEPAD, <9 mins>}, {TOUCHPAD, <2 mins>}, {TOUCHPAD, <3 mins>} }
+    // This would indicate that the GAMEPAD source was used first, and that source usage session
+    // lasted for 9 mins. During that time, the TOUCHPAD was used for 2 mins, until its source
+    // usage session expired. The TOUCHPAD was then used again later for another 3 mins.
+    using SourceUsageBreakdown =
+            std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>;
+
+    struct DeviceUsageReport {
+        std::chrono::nanoseconds usageDuration;
+        SourceUsageBreakdown sourceBreakdown;
+    };
+
     virtual void logInputDeviceUsageReported(const InputDeviceIdentifier&,
-                                             std::chrono::nanoseconds duration) = 0;
+                                             const DeviceUsageReport&) = 0;
     virtual ~InputDeviceMetricsLogger() = default;
 };
 
@@ -116,23 +134,42 @@
                       ftl::Orderable<DeviceId> {
         using Constructible::Constructible;
     };
-    static std::string toString(const DeviceId& id) {
+    static inline std::string toString(const DeviceId& id) {
         return std::to_string(ftl::to_underlying(id));
     }
 
-    std::map<DeviceId, InputDeviceIdentifier> mLoggedDeviceInfos;
+    std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;
 
-    struct UsageSession {
-        std::chrono::nanoseconds start;
-        std::chrono::nanoseconds end;
+    class ActiveSession {
+    public:
+        explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout,
+                               std::chrono::nanoseconds startTime);
+        void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source);
+        bool checkIfCompletedAt(std::chrono::nanoseconds timestamp);
+        InputDeviceMetricsLogger::DeviceUsageReport finishSession();
+
+    private:
+        struct UsageSession {
+            std::chrono::nanoseconds start{};
+            std::chrono::nanoseconds end{};
+        };
+
+        const std::chrono::nanoseconds mUsageSessionTimeout;
+        UsageSession mDeviceSession{};
+
+        std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{};
+        InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{};
     };
+
     // The input devices that currently have active usage sessions.
-    std::map<DeviceId, UsageSession> mActiveUsageSessions;
+    std::map<DeviceId, ActiveSession> mActiveUsageSessions;
 
     void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
     void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceIdentifier& identifier);
-    void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime);
-    void processUsages();
+    using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
+    void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
+                            const SourceProvider& getSources);
+    void reportCompletedSessions();
 };
 
 } // namespace android