xref: /aosp_15_r20/frameworks/native/services/inputflinger/InputDeviceMetricsCollector.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2023 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "InputDeviceMetricsCollector"
18*38e8c45fSAndroid Build Coastguard Worker #include "InputDeviceMetricsCollector.h"
19*38e8c45fSAndroid Build Coastguard Worker 
20*38e8c45fSAndroid Build Coastguard Worker #include "InputDeviceMetricsSource.h"
21*38e8c45fSAndroid Build Coastguard Worker 
22*38e8c45fSAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
23*38e8c45fSAndroid Build Coastguard Worker #include <input/PrintTools.h>
24*38e8c45fSAndroid Build Coastguard Worker 
25*38e8c45fSAndroid Build Coastguard Worker namespace android {
26*38e8c45fSAndroid Build Coastguard Worker 
27*38e8c45fSAndroid Build Coastguard Worker using android::base::StringPrintf;
28*38e8c45fSAndroid Build Coastguard Worker using std::chrono::nanoseconds;
29*38e8c45fSAndroid Build Coastguard Worker using std::chrono_literals::operator""ns;
30*38e8c45fSAndroid Build Coastguard Worker 
31*38e8c45fSAndroid Build Coastguard Worker namespace {
32*38e8c45fSAndroid Build Coastguard Worker 
33*38e8c45fSAndroid Build Coastguard Worker constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
34*38e8c45fSAndroid Build Coastguard Worker 
35*38e8c45fSAndroid Build Coastguard Worker /**
36*38e8c45fSAndroid Build Coastguard Worker  * Log debug messages about metrics events logged to statsd.
37*38e8c45fSAndroid Build Coastguard Worker  * Enable this via "adb shell setprop log.tag.InputDeviceMetricsCollector DEBUG" (requires restart)
38*38e8c45fSAndroid Build Coastguard Worker  */
39*38e8c45fSAndroid Build Coastguard Worker const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
40*38e8c45fSAndroid Build Coastguard Worker 
41*38e8c45fSAndroid Build Coastguard Worker constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
42*38e8c45fSAndroid Build Coastguard Worker 
linuxBusToInputDeviceBusEnum(int32_t linuxBus,bool isUsiStylus)43*38e8c45fSAndroid Build Coastguard Worker int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
44*38e8c45fSAndroid Build Coastguard Worker     if (isUsiStylus) {
45*38e8c45fSAndroid Build Coastguard Worker         // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
46*38e8c45fSAndroid Build Coastguard Worker         // For metrics purposes, we treat this protocol as a separate bus.
47*38e8c45fSAndroid Build Coastguard Worker         return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
48*38e8c45fSAndroid Build Coastguard Worker     }
49*38e8c45fSAndroid Build Coastguard Worker 
50*38e8c45fSAndroid Build Coastguard Worker     // When adding cases to this switch, also add them to the copy of this method in
51*38e8c45fSAndroid Build Coastguard Worker     // TouchpadInputMapper.cpp.
52*38e8c45fSAndroid Build Coastguard Worker     // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
53*38e8c45fSAndroid Build Coastguard Worker     switch (linuxBus) {
54*38e8c45fSAndroid Build Coastguard Worker         case BUS_USB:
55*38e8c45fSAndroid Build Coastguard Worker             return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
56*38e8c45fSAndroid Build Coastguard Worker         case BUS_BLUETOOTH:
57*38e8c45fSAndroid Build Coastguard Worker             return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
58*38e8c45fSAndroid Build Coastguard Worker         default:
59*38e8c45fSAndroid Build Coastguard Worker             return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
60*38e8c45fSAndroid Build Coastguard Worker     }
61*38e8c45fSAndroid Build Coastguard Worker }
62*38e8c45fSAndroid Build Coastguard Worker 
63*38e8c45fSAndroid Build Coastguard Worker class : public InputDeviceMetricsLogger {
getCurrentTime()64*38e8c45fSAndroid Build Coastguard Worker     nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
65*38e8c45fSAndroid Build Coastguard Worker 
logInputDeviceUsageReported(const MetricsDeviceInfo & info,const DeviceUsageReport & report)66*38e8c45fSAndroid Build Coastguard Worker     void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
67*38e8c45fSAndroid Build Coastguard Worker                                      const DeviceUsageReport& report) override {
68*38e8c45fSAndroid Build Coastguard Worker         const int32_t durationMillis =
69*38e8c45fSAndroid Build Coastguard Worker                 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
70*38e8c45fSAndroid Build Coastguard Worker         const static std::vector<int32_t> empty;
71*38e8c45fSAndroid Build Coastguard Worker 
72*38e8c45fSAndroid Build Coastguard Worker         ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
73*38e8c45fSAndroid Build Coastguard Worker         ALOGD_IF(DEBUG, "    Total duration: %dms", durationMillis);
74*38e8c45fSAndroid Build Coastguard Worker         ALOGD_IF(DEBUG, "    Source breakdown:");
75*38e8c45fSAndroid Build Coastguard Worker 
76*38e8c45fSAndroid Build Coastguard Worker         std::vector<int32_t> sources;
77*38e8c45fSAndroid Build Coastguard Worker         std::vector<int32_t> durationsPerSource;
78*38e8c45fSAndroid Build Coastguard Worker         for (auto& [src, dur] : report.sourceBreakdown) {
79*38e8c45fSAndroid Build Coastguard Worker             sources.push_back(ftl::to_underlying(src));
80*38e8c45fSAndroid Build Coastguard Worker             int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
81*38e8c45fSAndroid Build Coastguard Worker             durationsPerSource.emplace_back(durMillis);
82*38e8c45fSAndroid Build Coastguard Worker             ALOGD_IF(DEBUG, "        - usageSource: %s\t duration: %dms",
83*38e8c45fSAndroid Build Coastguard Worker                      ftl::enum_string(src).c_str(), durMillis);
84*38e8c45fSAndroid Build Coastguard Worker         }
85*38e8c45fSAndroid Build Coastguard Worker 
86*38e8c45fSAndroid Build Coastguard Worker         ALOGD_IF(DEBUG, "    Uid breakdown:");
87*38e8c45fSAndroid Build Coastguard Worker 
88*38e8c45fSAndroid Build Coastguard Worker         std::vector<int32_t> uids;
89*38e8c45fSAndroid Build Coastguard Worker         std::vector<int32_t> durationsPerUid;
90*38e8c45fSAndroid Build Coastguard Worker         for (auto& [uid, dur] : report.uidBreakdown) {
91*38e8c45fSAndroid Build Coastguard Worker             uids.push_back(uid.val());
92*38e8c45fSAndroid Build Coastguard Worker             int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
93*38e8c45fSAndroid Build Coastguard Worker             durationsPerUid.push_back(durMillis);
94*38e8c45fSAndroid Build Coastguard Worker             ALOGD_IF(DEBUG, "        - uid: %s\t duration: %dms", uid.toString().c_str(),
95*38e8c45fSAndroid Build Coastguard Worker                      durMillis);
96*38e8c45fSAndroid Build Coastguard Worker         }
97*38e8c45fSAndroid Build Coastguard Worker         util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
98*38e8c45fSAndroid Build Coastguard Worker                           linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
99*38e8c45fSAndroid Build Coastguard Worker                           sources, durationsPerSource, uids, durationsPerUid);
100*38e8c45fSAndroid Build Coastguard Worker     }
101*38e8c45fSAndroid Build Coastguard Worker } sStatsdLogger;
102*38e8c45fSAndroid Build Coastguard Worker 
isIgnoredInputDeviceId(int32_t deviceId)103*38e8c45fSAndroid Build Coastguard Worker bool isIgnoredInputDeviceId(int32_t deviceId) {
104*38e8c45fSAndroid Build Coastguard Worker     switch (deviceId) {
105*38e8c45fSAndroid Build Coastguard Worker         case INVALID_INPUT_DEVICE_ID:
106*38e8c45fSAndroid Build Coastguard Worker         case VIRTUAL_KEYBOARD_ID:
107*38e8c45fSAndroid Build Coastguard Worker             return true;
108*38e8c45fSAndroid Build Coastguard Worker         default:
109*38e8c45fSAndroid Build Coastguard Worker             return false;
110*38e8c45fSAndroid Build Coastguard Worker     }
111*38e8c45fSAndroid Build Coastguard Worker }
112*38e8c45fSAndroid Build Coastguard Worker 
113*38e8c45fSAndroid Build Coastguard Worker } // namespace
114*38e8c45fSAndroid Build Coastguard Worker 
InputDeviceMetricsCollector(InputListenerInterface & listener)115*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
116*38e8c45fSAndroid Build Coastguard Worker       : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
117*38e8c45fSAndroid Build Coastguard Worker 
InputDeviceMetricsCollector(InputListenerInterface & listener,InputDeviceMetricsLogger & logger,nanoseconds usageSessionTimeout)118*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
119*38e8c45fSAndroid Build Coastguard Worker                                                          InputDeviceMetricsLogger& logger,
120*38e8c45fSAndroid Build Coastguard Worker                                                          nanoseconds usageSessionTimeout)
121*38e8c45fSAndroid Build Coastguard Worker       : mNextListener(listener),
122*38e8c45fSAndroid Build Coastguard Worker         mLogger(logger),
123*38e8c45fSAndroid Build Coastguard Worker         mUsageSessionTimeout(usageSessionTimeout),
124*38e8c45fSAndroid Build Coastguard Worker         mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
125*38e8c45fSAndroid Build Coastguard Worker 
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)126*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyInputDevicesChanged(
127*38e8c45fSAndroid Build Coastguard Worker         const NotifyInputDevicesChangedArgs& args) {
128*38e8c45fSAndroid Build Coastguard Worker     {
129*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
130*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
131*38e8c45fSAndroid Build Coastguard Worker         onInputDevicesChanged(args.inputDeviceInfos);
132*38e8c45fSAndroid Build Coastguard Worker     }
133*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
134*38e8c45fSAndroid Build Coastguard Worker }
135*38e8c45fSAndroid Build Coastguard Worker 
notifyKey(const NotifyKeyArgs & args)136*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
137*38e8c45fSAndroid Build Coastguard Worker     {
138*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
139*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
140*38e8c45fSAndroid Build Coastguard Worker         const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
141*38e8c45fSAndroid Build Coastguard Worker             return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
142*38e8c45fSAndroid Build Coastguard Worker         };
143*38e8c45fSAndroid Build Coastguard Worker         onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
144*38e8c45fSAndroid Build Coastguard Worker     }
145*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
146*38e8c45fSAndroid Build Coastguard Worker }
147*38e8c45fSAndroid Build Coastguard Worker 
notifyMotion(const NotifyMotionArgs & args)148*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
149*38e8c45fSAndroid Build Coastguard Worker     {
150*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
151*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
152*38e8c45fSAndroid Build Coastguard Worker         onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
153*38e8c45fSAndroid Build Coastguard Worker                            [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
154*38e8c45fSAndroid Build Coastguard Worker     }
155*38e8c45fSAndroid Build Coastguard Worker 
156*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
157*38e8c45fSAndroid Build Coastguard Worker }
158*38e8c45fSAndroid Build Coastguard Worker 
notifySwitch(const NotifySwitchArgs & args)159*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
160*38e8c45fSAndroid Build Coastguard Worker     {
161*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
162*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
163*38e8c45fSAndroid Build Coastguard Worker     }
164*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
165*38e8c45fSAndroid Build Coastguard Worker }
166*38e8c45fSAndroid Build Coastguard Worker 
notifySensor(const NotifySensorArgs & args)167*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
168*38e8c45fSAndroid Build Coastguard Worker     {
169*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
170*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
171*38e8c45fSAndroid Build Coastguard Worker     }
172*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
173*38e8c45fSAndroid Build Coastguard Worker }
174*38e8c45fSAndroid Build Coastguard Worker 
notifyVibratorState(const NotifyVibratorStateArgs & args)175*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
176*38e8c45fSAndroid Build Coastguard Worker     {
177*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
178*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
179*38e8c45fSAndroid Build Coastguard Worker     }
180*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
181*38e8c45fSAndroid Build Coastguard Worker }
182*38e8c45fSAndroid Build Coastguard Worker 
notifyDeviceReset(const NotifyDeviceResetArgs & args)183*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
184*38e8c45fSAndroid Build Coastguard Worker     {
185*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
186*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
187*38e8c45fSAndroid Build Coastguard Worker     }
188*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
189*38e8c45fSAndroid Build Coastguard Worker }
190*38e8c45fSAndroid Build Coastguard Worker 
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)191*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
192*38e8c45fSAndroid Build Coastguard Worker         const NotifyPointerCaptureChangedArgs& args) {
193*38e8c45fSAndroid Build Coastguard Worker     {
194*38e8c45fSAndroid Build Coastguard Worker         std::scoped_lock lock(mLock);
195*38e8c45fSAndroid Build Coastguard Worker         reportCompletedSessions();
196*38e8c45fSAndroid Build Coastguard Worker     }
197*38e8c45fSAndroid Build Coastguard Worker     mNextListener.notify(args);
198*38e8c45fSAndroid Build Coastguard Worker }
199*38e8c45fSAndroid Build Coastguard Worker 
notifyDeviceInteraction(int32_t deviceId,nsecs_t timestamp,const std::set<Uid> & uids)200*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
201*38e8c45fSAndroid Build Coastguard Worker                                                           const std::set<Uid>& uids) {
202*38e8c45fSAndroid Build Coastguard Worker     if (isIgnoredInputDeviceId(deviceId)) {
203*38e8c45fSAndroid Build Coastguard Worker         return;
204*38e8c45fSAndroid Build Coastguard Worker     }
205*38e8c45fSAndroid Build Coastguard Worker     std::scoped_lock lock(mLock);
206*38e8c45fSAndroid Build Coastguard Worker     mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
207*38e8c45fSAndroid Build Coastguard Worker }
208*38e8c45fSAndroid Build Coastguard Worker 
dump(std::string & dump)209*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::dump(std::string& dump) {
210*38e8c45fSAndroid Build Coastguard Worker     std::scoped_lock lock(mLock);
211*38e8c45fSAndroid Build Coastguard Worker     dump += "InputDeviceMetricsCollector:\n";
212*38e8c45fSAndroid Build Coastguard Worker 
213*38e8c45fSAndroid Build Coastguard Worker     dump += "  Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
214*38e8c45fSAndroid Build Coastguard Worker     dump += "  Devices with active usage sessions: " +
215*38e8c45fSAndroid Build Coastguard Worker             dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
216*38e8c45fSAndroid Build Coastguard Worker }
217*38e8c45fSAndroid Build Coastguard Worker 
monitor()218*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::monitor() {
219*38e8c45fSAndroid Build Coastguard Worker     std::scoped_lock lock(mLock);
220*38e8c45fSAndroid Build Coastguard Worker }
221*38e8c45fSAndroid Build Coastguard Worker 
onInputDevicesChanged(const std::vector<InputDeviceInfo> & infos)222*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
223*38e8c45fSAndroid Build Coastguard Worker     std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
224*38e8c45fSAndroid Build Coastguard Worker 
225*38e8c45fSAndroid Build Coastguard Worker     for (const InputDeviceInfo& info : infos) {
226*38e8c45fSAndroid Build Coastguard Worker         if (isIgnoredInputDeviceId(info.getId())) {
227*38e8c45fSAndroid Build Coastguard Worker             continue;
228*38e8c45fSAndroid Build Coastguard Worker         }
229*38e8c45fSAndroid Build Coastguard Worker         const auto& i = info.getIdentifier();
230*38e8c45fSAndroid Build Coastguard Worker         newDeviceInfos.emplace(info.getId(),
231*38e8c45fSAndroid Build Coastguard Worker                                MetricsDeviceInfo{
232*38e8c45fSAndroid Build Coastguard Worker                                        .deviceId = info.getId(),
233*38e8c45fSAndroid Build Coastguard Worker                                        .vendor = i.vendor,
234*38e8c45fSAndroid Build Coastguard Worker                                        .product = i.product,
235*38e8c45fSAndroid Build Coastguard Worker                                        .version = i.version,
236*38e8c45fSAndroid Build Coastguard Worker                                        .bus = i.bus,
237*38e8c45fSAndroid Build Coastguard Worker                                        .isUsiStylus = info.getUsiVersion().has_value(),
238*38e8c45fSAndroid Build Coastguard Worker                                        .keyboardType = info.getKeyboardType(),
239*38e8c45fSAndroid Build Coastguard Worker                                });
240*38e8c45fSAndroid Build Coastguard Worker     }
241*38e8c45fSAndroid Build Coastguard Worker 
242*38e8c45fSAndroid Build Coastguard Worker     for (auto [deviceId, info] : mLoggedDeviceInfos) {
243*38e8c45fSAndroid Build Coastguard Worker         if (newDeviceInfos.count(deviceId) != 0) {
244*38e8c45fSAndroid Build Coastguard Worker             continue;
245*38e8c45fSAndroid Build Coastguard Worker         }
246*38e8c45fSAndroid Build Coastguard Worker         onInputDeviceRemoved(deviceId, info);
247*38e8c45fSAndroid Build Coastguard Worker     }
248*38e8c45fSAndroid Build Coastguard Worker 
249*38e8c45fSAndroid Build Coastguard Worker     std::swap(newDeviceInfos, mLoggedDeviceInfos);
250*38e8c45fSAndroid Build Coastguard Worker }
251*38e8c45fSAndroid Build Coastguard Worker 
onInputDeviceRemoved(DeviceId deviceId,const MetricsDeviceInfo & info)252*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
253*38e8c45fSAndroid Build Coastguard Worker                                                        const MetricsDeviceInfo& info) {
254*38e8c45fSAndroid Build Coastguard Worker     auto it = mActiveUsageSessions.find(deviceId);
255*38e8c45fSAndroid Build Coastguard Worker     if (it == mActiveUsageSessions.end()) {
256*38e8c45fSAndroid Build Coastguard Worker         return;
257*38e8c45fSAndroid Build Coastguard Worker     }
258*38e8c45fSAndroid Build Coastguard Worker     // Report usage for that device if there is an active session.
259*38e8c45fSAndroid Build Coastguard Worker     auto& [_, activeSession] = *it;
260*38e8c45fSAndroid Build Coastguard Worker     mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
261*38e8c45fSAndroid Build Coastguard Worker     mActiveUsageSessions.erase(it);
262*38e8c45fSAndroid Build Coastguard Worker 
263*38e8c45fSAndroid Build Coastguard Worker     // We don't remove this from mLoggedDeviceInfos because it will be updated in
264*38e8c45fSAndroid Build Coastguard Worker     // onInputDevicesChanged().
265*38e8c45fSAndroid Build Coastguard Worker }
266*38e8c45fSAndroid Build Coastguard Worker 
onInputDeviceUsage(DeviceId deviceId,nanoseconds eventTime,const SourceProvider & getSources)267*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
268*38e8c45fSAndroid Build Coastguard Worker                                                      const SourceProvider& getSources) {
269*38e8c45fSAndroid Build Coastguard Worker     auto infoIt = mLoggedDeviceInfos.find(deviceId);
270*38e8c45fSAndroid Build Coastguard Worker     if (infoIt == mLoggedDeviceInfos.end()) {
271*38e8c45fSAndroid Build Coastguard Worker         // Do not track usage for devices that are not logged.
272*38e8c45fSAndroid Build Coastguard Worker         return;
273*38e8c45fSAndroid Build Coastguard Worker     }
274*38e8c45fSAndroid Build Coastguard Worker 
275*38e8c45fSAndroid Build Coastguard Worker     auto [sessionIt, _] =
276*38e8c45fSAndroid Build Coastguard Worker             mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
277*38e8c45fSAndroid Build Coastguard Worker     for (InputDeviceUsageSource source : getSources(infoIt->second)) {
278*38e8c45fSAndroid Build Coastguard Worker         sessionIt->second.recordUsage(eventTime, source);
279*38e8c45fSAndroid Build Coastguard Worker     }
280*38e8c45fSAndroid Build Coastguard Worker }
281*38e8c45fSAndroid Build Coastguard Worker 
onInputDeviceInteraction(const Interaction & interaction)282*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
283*38e8c45fSAndroid Build Coastguard Worker     auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
284*38e8c45fSAndroid Build Coastguard Worker     if (activeSessionIt == mActiveUsageSessions.end()) {
285*38e8c45fSAndroid Build Coastguard Worker         return;
286*38e8c45fSAndroid Build Coastguard Worker     }
287*38e8c45fSAndroid Build Coastguard Worker 
288*38e8c45fSAndroid Build Coastguard Worker     activeSessionIt->second.recordInteraction(interaction);
289*38e8c45fSAndroid Build Coastguard Worker }
290*38e8c45fSAndroid Build Coastguard Worker 
reportCompletedSessions()291*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::reportCompletedSessions() {
292*38e8c45fSAndroid Build Coastguard Worker     // Process all pending interactions.
293*38e8c45fSAndroid Build Coastguard Worker     for (auto interaction = mInteractionsQueue.pop(); interaction;
294*38e8c45fSAndroid Build Coastguard Worker          interaction = mInteractionsQueue.pop()) {
295*38e8c45fSAndroid Build Coastguard Worker         onInputDeviceInteraction(*interaction);
296*38e8c45fSAndroid Build Coastguard Worker     }
297*38e8c45fSAndroid Build Coastguard Worker 
298*38e8c45fSAndroid Build Coastguard Worker     const auto currentTime = mLogger.getCurrentTime();
299*38e8c45fSAndroid Build Coastguard Worker     std::vector<DeviceId> completedUsageSessions;
300*38e8c45fSAndroid Build Coastguard Worker 
301*38e8c45fSAndroid Build Coastguard Worker     // Process usages for all active session to determine if any sessions have expired.
302*38e8c45fSAndroid Build Coastguard Worker     for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
303*38e8c45fSAndroid Build Coastguard Worker         if (activeSession.checkIfCompletedAt(currentTime)) {
304*38e8c45fSAndroid Build Coastguard Worker             completedUsageSessions.emplace_back(deviceId);
305*38e8c45fSAndroid Build Coastguard Worker         }
306*38e8c45fSAndroid Build Coastguard Worker     }
307*38e8c45fSAndroid Build Coastguard Worker 
308*38e8c45fSAndroid Build Coastguard Worker     // Close out and log all expired usage sessions.
309*38e8c45fSAndroid Build Coastguard Worker     for (DeviceId deviceId : completedUsageSessions) {
310*38e8c45fSAndroid Build Coastguard Worker         const auto infoIt = mLoggedDeviceInfos.find(deviceId);
311*38e8c45fSAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
312*38e8c45fSAndroid Build Coastguard Worker 
313*38e8c45fSAndroid Build Coastguard Worker         auto activeSessionIt = mActiveUsageSessions.find(deviceId);
314*38e8c45fSAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
315*38e8c45fSAndroid Build Coastguard Worker         auto& [_, activeSession] = *activeSessionIt;
316*38e8c45fSAndroid Build Coastguard Worker         mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
317*38e8c45fSAndroid Build Coastguard Worker         mActiveUsageSessions.erase(activeSessionIt);
318*38e8c45fSAndroid Build Coastguard Worker     }
319*38e8c45fSAndroid Build Coastguard Worker }
320*38e8c45fSAndroid Build Coastguard Worker 
321*38e8c45fSAndroid Build Coastguard Worker // --- InputDeviceMetricsCollector::ActiveSession ---
322*38e8c45fSAndroid Build Coastguard Worker 
ActiveSession(nanoseconds usageSessionTimeout,nanoseconds startTime)323*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
324*38e8c45fSAndroid Build Coastguard Worker                                                           nanoseconds startTime)
325*38e8c45fSAndroid Build Coastguard Worker       : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
326*38e8c45fSAndroid Build Coastguard Worker 
recordUsage(nanoseconds eventTime,InputDeviceUsageSource source)327*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
328*38e8c45fSAndroid Build Coastguard Worker                                                              InputDeviceUsageSource source) {
329*38e8c45fSAndroid Build Coastguard Worker     // We assume that event times for subsequent events are always monotonically increasing for each
330*38e8c45fSAndroid Build Coastguard Worker     // input device.
331*38e8c45fSAndroid Build Coastguard Worker     auto [activeSourceIt, inserted] =
332*38e8c45fSAndroid Build Coastguard Worker             mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
333*38e8c45fSAndroid Build Coastguard Worker     if (!inserted) {
334*38e8c45fSAndroid Build Coastguard Worker         activeSourceIt->second.end = eventTime;
335*38e8c45fSAndroid Build Coastguard Worker     }
336*38e8c45fSAndroid Build Coastguard Worker     mDeviceSession.end = eventTime;
337*38e8c45fSAndroid Build Coastguard Worker }
338*38e8c45fSAndroid Build Coastguard Worker 
recordInteraction(const Interaction & interaction)339*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
340*38e8c45fSAndroid Build Coastguard Worker     const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
341*38e8c45fSAndroid Build Coastguard Worker     const auto timestamp = std::get<nanoseconds>(interaction);
342*38e8c45fSAndroid Build Coastguard Worker     if (timestamp >= sessionExpiryTime) {
343*38e8c45fSAndroid Build Coastguard Worker         // This interaction occurred after the device's current active session is set to expire.
344*38e8c45fSAndroid Build Coastguard Worker         // Ignore it.
345*38e8c45fSAndroid Build Coastguard Worker         return;
346*38e8c45fSAndroid Build Coastguard Worker     }
347*38e8c45fSAndroid Build Coastguard Worker 
348*38e8c45fSAndroid Build Coastguard Worker     for (Uid uid : std::get<std::set<Uid>>(interaction)) {
349*38e8c45fSAndroid Build Coastguard Worker         auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
350*38e8c45fSAndroid Build Coastguard Worker         if (!inserted) {
351*38e8c45fSAndroid Build Coastguard Worker             activeUidIt->second.end = timestamp;
352*38e8c45fSAndroid Build Coastguard Worker         }
353*38e8c45fSAndroid Build Coastguard Worker     }
354*38e8c45fSAndroid Build Coastguard Worker }
355*38e8c45fSAndroid Build Coastguard Worker 
checkIfCompletedAt(nanoseconds timestamp)356*38e8c45fSAndroid Build Coastguard Worker bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
357*38e8c45fSAndroid Build Coastguard Worker     const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
358*38e8c45fSAndroid Build Coastguard Worker     std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
359*38e8c45fSAndroid Build Coastguard Worker     for (auto& [source, session] : mActiveSessionsBySource) {
360*38e8c45fSAndroid Build Coastguard Worker         if (session.end <= sessionExpiryTime) {
361*38e8c45fSAndroid Build Coastguard Worker             completedSourceSessionsForDevice.emplace_back(source);
362*38e8c45fSAndroid Build Coastguard Worker         }
363*38e8c45fSAndroid Build Coastguard Worker     }
364*38e8c45fSAndroid Build Coastguard Worker     for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
365*38e8c45fSAndroid Build Coastguard Worker         auto it = mActiveSessionsBySource.find(source);
366*38e8c45fSAndroid Build Coastguard Worker         const auto& [_, session] = *it;
367*38e8c45fSAndroid Build Coastguard Worker         mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
368*38e8c45fSAndroid Build Coastguard Worker         mActiveSessionsBySource.erase(it);
369*38e8c45fSAndroid Build Coastguard Worker     }
370*38e8c45fSAndroid Build Coastguard Worker 
371*38e8c45fSAndroid Build Coastguard Worker     std::vector<Uid> completedUidSessionsForDevice;
372*38e8c45fSAndroid Build Coastguard Worker     for (auto& [uid, session] : mActiveSessionsByUid) {
373*38e8c45fSAndroid Build Coastguard Worker         if (session.end <= sessionExpiryTime) {
374*38e8c45fSAndroid Build Coastguard Worker             completedUidSessionsForDevice.emplace_back(uid);
375*38e8c45fSAndroid Build Coastguard Worker         }
376*38e8c45fSAndroid Build Coastguard Worker     }
377*38e8c45fSAndroid Build Coastguard Worker     for (Uid uid : completedUidSessionsForDevice) {
378*38e8c45fSAndroid Build Coastguard Worker         auto it = mActiveSessionsByUid.find(uid);
379*38e8c45fSAndroid Build Coastguard Worker         const auto& [_, session] = *it;
380*38e8c45fSAndroid Build Coastguard Worker         mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
381*38e8c45fSAndroid Build Coastguard Worker         mActiveSessionsByUid.erase(it);
382*38e8c45fSAndroid Build Coastguard Worker     }
383*38e8c45fSAndroid Build Coastguard Worker 
384*38e8c45fSAndroid Build Coastguard Worker     // This active session has expired if there are no more active source sessions tracked.
385*38e8c45fSAndroid Build Coastguard Worker     return mActiveSessionsBySource.empty();
386*38e8c45fSAndroid Build Coastguard Worker }
387*38e8c45fSAndroid Build Coastguard Worker 
388*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsLogger::DeviceUsageReport
finishSession()389*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::ActiveSession::finishSession() {
390*38e8c45fSAndroid Build Coastguard Worker     const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
391*38e8c45fSAndroid Build Coastguard Worker 
392*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
393*38e8c45fSAndroid Build Coastguard Worker         mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
394*38e8c45fSAndroid Build Coastguard Worker     }
395*38e8c45fSAndroid Build Coastguard Worker     mActiveSessionsBySource.clear();
396*38e8c45fSAndroid Build Coastguard Worker 
397*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
398*38e8c45fSAndroid Build Coastguard Worker         mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
399*38e8c45fSAndroid Build Coastguard Worker     }
400*38e8c45fSAndroid Build Coastguard Worker     mActiveSessionsByUid.clear();
401*38e8c45fSAndroid Build Coastguard Worker 
402*38e8c45fSAndroid Build Coastguard Worker     return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
403*38e8c45fSAndroid Build Coastguard Worker }
404*38e8c45fSAndroid Build Coastguard Worker 
405*38e8c45fSAndroid Build Coastguard Worker } // namespace android
406