blob: 8e04150efa02f063c873ea96378336970f914b9a [file] [log] [blame]
Prabir Pradhanaddf8e92023-04-06 00:28:48 +00001/*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "InputDeviceMetricsCollector"
18#include "InputDeviceMetricsCollector.h"
19
Prabir Pradhanae10ee62023-05-12 19:44:18 +000020#include "KeyCodeClassifications.h"
21
Prabir Pradhan852db892023-04-06 22:16:44 +000022#include <android-base/stringprintf.h>
23#include <input/PrintTools.h>
24#include <linux/input.h>
Prabir Pradhan852db892023-04-06 22:16:44 +000025
Prabir Pradhanaddf8e92023-04-06 00:28:48 +000026namespace android {
27
Prabir Pradhan852db892023-04-06 22:16:44 +000028using android::base::StringPrintf;
29using std::chrono::nanoseconds;
Prabir Pradhan44293792023-05-08 19:37:44 +000030using std::chrono_literals::operator""ns;
Prabir Pradhan852db892023-04-06 22:16:44 +000031
32namespace {
33
Prabir Pradhan7adabad2023-06-28 16:16:27 +000034constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
Prabir Pradhan852db892023-04-06 22:16:44 +000035
36/**
37 * Log debug messages about metrics events logged to statsd.
38 * Enable this via "adb shell setprop log.tag.InputDeviceMetricsCollector DEBUG" (requires restart)
39 */
40const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
41
Prabir Pradhan047695b2023-06-30 01:48:45 +000042constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
43
Prabir Pradhan67d09ca2023-09-08 20:28:55 +000044int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
45 if (isUsiStylus) {
46 // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
47 // For metrics purposes, we treat this protocol as a separate bus.
48 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
49 }
50
Harry Cuttsa34de522023-06-06 15:52:54 +000051 // When adding cases to this switch, also add them to the copy of this method in
52 // TouchpadInputMapper.cpp.
53 // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
Prabir Pradhan852db892023-04-06 22:16:44 +000054 switch (linuxBus) {
55 case BUS_USB:
56 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
57 case BUS_BLUETOOTH:
58 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
59 default:
60 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
61 }
62}
63
64class : public InputDeviceMetricsLogger {
65 nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
66
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000067 void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
Prabir Pradhanaff13032023-04-07 22:25:03 +000068 const DeviceUsageReport& report) override {
Prabir Pradhan852db892023-04-06 22:16:44 +000069 const int32_t durationMillis =
Prabir Pradhanaff13032023-04-07 22:25:03 +000070 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
Prabir Pradhan852db892023-04-06 22:16:44 +000071 const static std::vector<int32_t> empty;
72
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000073 ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
Prabir Pradhan852db892023-04-06 22:16:44 +000074 ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
Prabir Pradhanaff13032023-04-07 22:25:03 +000075 ALOGD_IF(DEBUG, " Source breakdown:");
76
77 std::vector<int32_t> sources;
78 std::vector<int32_t> durationsPerSource;
79 for (auto& [src, dur] : report.sourceBreakdown) {
80 sources.push_back(ftl::to_underlying(src));
81 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
82 durationsPerSource.emplace_back(durMillis);
83 ALOGD_IF(DEBUG, " - usageSource: %s\t duration: %dms",
84 ftl::enum_string(src).c_str(), durMillis);
85 }
Prabir Pradhan852db892023-04-06 22:16:44 +000086
Prabir Pradhan44293792023-05-08 19:37:44 +000087 ALOGD_IF(DEBUG, " Uid breakdown:");
88
89 std::vector<int32_t> uids;
90 std::vector<int32_t> durationsPerUid;
91 for (auto& [uid, dur] : report.uidBreakdown) {
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +000092 uids.push_back(uid.val());
Prabir Pradhan44293792023-05-08 19:37:44 +000093 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
94 durationsPerUid.push_back(durMillis);
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +000095 ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
96 durMillis);
Prabir Pradhan44293792023-05-08 19:37:44 +000097 }
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000098 util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
99 linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
100 sources, durationsPerSource, uids, durationsPerUid);
Prabir Pradhan852db892023-04-06 22:16:44 +0000101 }
102} sStatsdLogger;
103
104bool isIgnoredInputDeviceId(int32_t deviceId) {
105 switch (deviceId) {
106 case INVALID_INPUT_DEVICE_ID:
107 case VIRTUAL_KEYBOARD_ID:
108 return true;
109 default:
110 return false;
111 }
112}
113
114} // namespace
115
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000116InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
Prabir Pradhanae10ee62023-05-12 19:44:18 +0000117 const NotifyKeyArgs& keyArgs) {
118 if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
119 return InputDeviceUsageSource::UNKNOWN;
120 }
121
122 if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
123 DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
124 return InputDeviceUsageSource::DPAD;
125 }
126
127 if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
128 GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
129 return InputDeviceUsageSource::GAMEPAD;
130 }
131
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000132 if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
Prabir Pradhanae10ee62023-05-12 19:44:18 +0000133 return InputDeviceUsageSource::KEYBOARD;
134 }
135
136 return InputDeviceUsageSource::BUTTONS;
137}
138
139std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
Siarhei Vishniakou3218fc02023-06-15 20:41:02 -0700140 LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
Prabir Pradhanae10ee62023-05-12 19:44:18 +0000141 std::set<InputDeviceUsageSource> sources;
142
Siarhei Vishniakou3218fc02023-06-15 20:41:02 -0700143 for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
Prabir Pradhanae10ee62023-05-12 19:44:18 +0000144 const auto toolType = motionArgs.pointerProperties[i].toolType;
145 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
146 if (toolType == ToolType::MOUSE) {
147 sources.emplace(InputDeviceUsageSource::MOUSE);
148 continue;
149 }
150 if (toolType == ToolType::FINGER) {
151 sources.emplace(InputDeviceUsageSource::TOUCHPAD);
152 continue;
153 }
154 if (isStylusToolType(toolType)) {
155 sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
156 continue;
157 }
158 }
159 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
160 toolType == ToolType::MOUSE) {
161 sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
162 continue;
163 }
164 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
165 toolType == ToolType::FINGER) {
166 sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
167 continue;
168 }
169 if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
170 isStylusToolType(toolType)) {
171 sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
172 continue;
173 }
174 if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
175 sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
176 continue;
177 }
178 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
179 sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
180 continue;
181 }
182 if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
183 sources.emplace(InputDeviceUsageSource::JOYSTICK);
184 continue;
185 }
186 if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
187 sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
188 continue;
189 }
190 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
191 sources.emplace(InputDeviceUsageSource::TRACKBALL);
192 continue;
193 }
194 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
195 sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
196 continue;
197 }
198 sources.emplace(InputDeviceUsageSource::UNKNOWN);
199 }
200
201 return sources;
202}
203
204// --- InputDeviceMetricsCollector ---
205
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000206InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
Prabir Pradhan852db892023-04-06 22:16:44 +0000207 : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
208
209InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
210 InputDeviceMetricsLogger& logger,
211 nanoseconds usageSessionTimeout)
Prabir Pradhan047695b2023-06-30 01:48:45 +0000212 : mNextListener(listener),
213 mLogger(logger),
214 mUsageSessionTimeout(usageSessionTimeout),
215 mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000216
217void InputDeviceMetricsCollector::notifyInputDevicesChanged(
218 const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000219 reportCompletedSessions();
Prabir Pradhan852db892023-04-06 22:16:44 +0000220 onInputDevicesChanged(args.inputDeviceInfos);
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000221 mNextListener.notify(args);
222}
223
224void InputDeviceMetricsCollector::notifyConfigurationChanged(
225 const NotifyConfigurationChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000226 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000227 mNextListener.notify(args);
228}
229
230void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000231 reportCompletedSessions();
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000232 const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
233 return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
Prabir Pradhanaff13032023-04-07 22:25:03 +0000234 };
235 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
Prabir Pradhan852db892023-04-06 22:16:44 +0000236
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000237 mNextListener.notify(args);
238}
239
240void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000241 reportCompletedSessions();
242 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
243 [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
Prabir Pradhan852db892023-04-06 22:16:44 +0000244
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000245 mNextListener.notify(args);
246}
247
248void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000249 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000250 mNextListener.notify(args);
251}
252
253void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000254 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000255 mNextListener.notify(args);
256}
257
258void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000259 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000260 mNextListener.notify(args);
261}
262
263void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000264 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000265 mNextListener.notify(args);
266}
267
268void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
269 const NotifyPointerCaptureChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000270 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000271 mNextListener.notify(args);
272}
273
Prabir Pradhan8ede1d12023-05-08 19:37:44 +0000274void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +0000275 const std::set<Uid>& uids) {
Prabir Pradhan047695b2023-06-30 01:48:45 +0000276 if (isIgnoredInputDeviceId(deviceId)) {
277 return;
278 }
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +0000279 mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
Prabir Pradhan8ede1d12023-05-08 19:37:44 +0000280}
281
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000282void InputDeviceMetricsCollector::dump(std::string& dump) {
283 dump += "InputDeviceMetricsCollector:\n";
Prabir Pradhan852db892023-04-06 22:16:44 +0000284
285 dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
286 dump += " Devices with active usage sessions: " +
287 dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
288}
289
290void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000291 std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
Prabir Pradhan852db892023-04-06 22:16:44 +0000292
293 for (const InputDeviceInfo& info : infos) {
294 if (isIgnoredInputDeviceId(info.getId())) {
295 continue;
296 }
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000297 const auto& i = info.getIdentifier();
298 newDeviceInfos.emplace(info.getId(),
299 MetricsDeviceInfo{
300 .deviceId = info.getId(),
301 .vendor = i.vendor,
302 .product = i.product,
303 .version = i.version,
304 .bus = i.bus,
305 .isUsiStylus = info.getUsiVersion().has_value(),
306 .keyboardType = info.getKeyboardType(),
307 });
Prabir Pradhan852db892023-04-06 22:16:44 +0000308 }
309
Prabir Pradhanaff13032023-04-07 22:25:03 +0000310 for (auto [deviceId, info] : mLoggedDeviceInfos) {
311 if (newDeviceInfos.count(deviceId) != 0) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000312 continue;
313 }
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000314 onInputDeviceRemoved(deviceId, info);
Prabir Pradhan852db892023-04-06 22:16:44 +0000315 }
316
Prabir Pradhanaff13032023-04-07 22:25:03 +0000317 std::swap(newDeviceInfos, mLoggedDeviceInfos);
Prabir Pradhan852db892023-04-06 22:16:44 +0000318}
319
320void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000321 const MetricsDeviceInfo& info) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000322 auto it = mActiveUsageSessions.find(deviceId);
Prabir Pradhanaff13032023-04-07 22:25:03 +0000323 if (it == mActiveUsageSessions.end()) {
324 return;
Prabir Pradhan852db892023-04-06 22:16:44 +0000325 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000326 // Report usage for that device if there is an active session.
327 auto& [_, activeSession] = *it;
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000328 mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
Prabir Pradhanaff13032023-04-07 22:25:03 +0000329 mActiveUsageSessions.erase(it);
330
Prabir Pradhan852db892023-04-06 22:16:44 +0000331 // We don't remove this from mLoggedDeviceInfos because it will be updated in
332 // onInputDevicesChanged().
333}
334
Prabir Pradhanaff13032023-04-07 22:25:03 +0000335void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
336 const SourceProvider& getSources) {
337 auto infoIt = mLoggedDeviceInfos.find(deviceId);
338 if (infoIt == mLoggedDeviceInfos.end()) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000339 // Do not track usage for devices that are not logged.
340 return;
341 }
342
Prabir Pradhanaff13032023-04-07 22:25:03 +0000343 auto [sessionIt, _] =
344 mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
345 for (InputDeviceUsageSource source : getSources(infoIt->second)) {
346 sessionIt->second.recordUsage(eventTime, source);
Prabir Pradhan852db892023-04-06 22:16:44 +0000347 }
348}
349
Prabir Pradhan44293792023-05-08 19:37:44 +0000350void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
351 auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
352 if (activeSessionIt == mActiveUsageSessions.end()) {
353 return;
354 }
Prabir Pradhan852db892023-04-06 22:16:44 +0000355
Prabir Pradhan44293792023-05-08 19:37:44 +0000356 activeSessionIt->second.recordInteraction(interaction);
357}
358
359void InputDeviceMetricsCollector::reportCompletedSessions() {
360 // Process all pending interactions.
361 for (auto interaction = mInteractionsQueue.pop(); interaction;
362 interaction = mInteractionsQueue.pop()) {
363 onInputDeviceInteraction(*interaction);
364 }
365
366 const auto currentTime = mLogger.getCurrentTime();
Prabir Pradhan852db892023-04-06 22:16:44 +0000367 std::vector<DeviceId> completedUsageSessions;
368
Prabir Pradhan44293792023-05-08 19:37:44 +0000369 // Process usages for all active session to determine if any sessions have expired.
Prabir Pradhanaff13032023-04-07 22:25:03 +0000370 for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
371 if (activeSession.checkIfCompletedAt(currentTime)) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000372 completedUsageSessions.emplace_back(deviceId);
373 }
374 }
375
Prabir Pradhan44293792023-05-08 19:37:44 +0000376 // Close out and log all expired usage sessions.
Prabir Pradhan852db892023-04-06 22:16:44 +0000377 for (DeviceId deviceId : completedUsageSessions) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000378 const auto infoIt = mLoggedDeviceInfos.find(deviceId);
379 LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
Prabir Pradhan852db892023-04-06 22:16:44 +0000380
Prabir Pradhanaff13032023-04-07 22:25:03 +0000381 auto activeSessionIt = mActiveUsageSessions.find(deviceId);
382 LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
383 auto& [_, activeSession] = *activeSessionIt;
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000384 mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
Prabir Pradhanaff13032023-04-07 22:25:03 +0000385 mActiveUsageSessions.erase(activeSessionIt);
Prabir Pradhan852db892023-04-06 22:16:44 +0000386 }
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000387}
388
Prabir Pradhanaff13032023-04-07 22:25:03 +0000389// --- InputDeviceMetricsCollector::ActiveSession ---
390
391InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
392 nanoseconds startTime)
393 : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
394
395void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
396 InputDeviceUsageSource source) {
397 // We assume that event times for subsequent events are always monotonically increasing for each
398 // input device.
399 auto [activeSourceIt, inserted] =
400 mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
401 if (!inserted) {
402 activeSourceIt->second.end = eventTime;
403 }
404 mDeviceSession.end = eventTime;
405}
406
Prabir Pradhan44293792023-05-08 19:37:44 +0000407void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
408 const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
409 const auto timestamp = std::get<nanoseconds>(interaction);
410 if (timestamp >= sessionExpiryTime) {
411 // This interaction occurred after the device's current active session is set to expire.
412 // Ignore it.
413 return;
414 }
415
416 for (Uid uid : std::get<std::set<Uid>>(interaction)) {
417 auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
418 if (!inserted) {
419 activeUidIt->second.end = timestamp;
420 }
421 }
422}
423
Prabir Pradhanaff13032023-04-07 22:25:03 +0000424bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
425 const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
426 std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
427 for (auto& [source, session] : mActiveSessionsBySource) {
428 if (session.end <= sessionExpiryTime) {
429 completedSourceSessionsForDevice.emplace_back(source);
430 }
431 }
432 for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
433 auto it = mActiveSessionsBySource.find(source);
434 const auto& [_, session] = *it;
435 mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
436 mActiveSessionsBySource.erase(it);
437 }
Prabir Pradhan44293792023-05-08 19:37:44 +0000438
439 std::vector<Uid> completedUidSessionsForDevice;
440 for (auto& [uid, session] : mActiveSessionsByUid) {
441 if (session.end <= sessionExpiryTime) {
442 completedUidSessionsForDevice.emplace_back(uid);
443 }
444 }
445 for (Uid uid : completedUidSessionsForDevice) {
446 auto it = mActiveSessionsByUid.find(uid);
447 const auto& [_, session] = *it;
448 mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
449 mActiveSessionsByUid.erase(it);
450 }
451
452 // This active session has expired if there are no more active source sessions tracked.
Prabir Pradhanaff13032023-04-07 22:25:03 +0000453 return mActiveSessionsBySource.empty();
454}
455
456InputDeviceMetricsLogger::DeviceUsageReport
457InputDeviceMetricsCollector::ActiveSession::finishSession() {
458 const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
459
460 for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
461 mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
462 }
463 mActiveSessionsBySource.clear();
464
Prabir Pradhan44293792023-05-08 19:37:44 +0000465 for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
466 mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
467 }
468 mActiveSessionsByUid.clear();
469
470 return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
Prabir Pradhanaff13032023-04-07 22:25:03 +0000471}
472
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000473} // namespace android