blob: cefb14059cc534007363188a575b8bda5b799282 [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
Asmita Poddar631a14e2023-10-03 10:22:07 +000020#include "InputDeviceMetricsSource.h"
Prabir Pradhanae10ee62023-05-12 19:44:18 +000021
Prabir Pradhan852db892023-04-06 22:16:44 +000022#include <android-base/stringprintf.h>
23#include <input/PrintTools.h>
Prabir Pradhan852db892023-04-06 22:16:44 +000024
Prabir Pradhanaddf8e92023-04-06 00:28:48 +000025namespace android {
26
Prabir Pradhan852db892023-04-06 22:16:44 +000027using android::base::StringPrintf;
28using std::chrono::nanoseconds;
Prabir Pradhan44293792023-05-08 19:37:44 +000029using std::chrono_literals::operator""ns;
Prabir Pradhan852db892023-04-06 22:16:44 +000030
31namespace {
32
Prabir Pradhan7adabad2023-06-28 16:16:27 +000033constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
Prabir Pradhan852db892023-04-06 22:16:44 +000034
35/**
36 * Log debug messages about metrics events logged to statsd.
37 * Enable this via "adb shell setprop log.tag.InputDeviceMetricsCollector DEBUG" (requires restart)
38 */
39const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
40
Prabir Pradhan047695b2023-06-30 01:48:45 +000041constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
42
Prabir Pradhan67d09ca2023-09-08 20:28:55 +000043int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
44 if (isUsiStylus) {
45 // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
46 // For metrics purposes, we treat this protocol as a separate bus.
47 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
48 }
49
Harry Cuttsa34de522023-06-06 15:52:54 +000050 // When adding cases to this switch, also add them to the copy of this method in
51 // TouchpadInputMapper.cpp.
52 // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
Prabir Pradhan852db892023-04-06 22:16:44 +000053 switch (linuxBus) {
54 case BUS_USB:
55 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
56 case BUS_BLUETOOTH:
57 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
58 default:
59 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
60 }
61}
62
63class : public InputDeviceMetricsLogger {
64 nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
65
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000066 void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
Prabir Pradhanaff13032023-04-07 22:25:03 +000067 const DeviceUsageReport& report) override {
Prabir Pradhan852db892023-04-06 22:16:44 +000068 const int32_t durationMillis =
Prabir Pradhanaff13032023-04-07 22:25:03 +000069 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
Prabir Pradhan852db892023-04-06 22:16:44 +000070 const static std::vector<int32_t> empty;
71
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000072 ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
Prabir Pradhan852db892023-04-06 22:16:44 +000073 ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
Prabir Pradhanaff13032023-04-07 22:25:03 +000074 ALOGD_IF(DEBUG, " Source breakdown:");
75
76 std::vector<int32_t> sources;
77 std::vector<int32_t> durationsPerSource;
78 for (auto& [src, dur] : report.sourceBreakdown) {
79 sources.push_back(ftl::to_underlying(src));
80 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
81 durationsPerSource.emplace_back(durMillis);
82 ALOGD_IF(DEBUG, " - usageSource: %s\t duration: %dms",
83 ftl::enum_string(src).c_str(), durMillis);
84 }
Prabir Pradhan852db892023-04-06 22:16:44 +000085
Prabir Pradhan44293792023-05-08 19:37:44 +000086 ALOGD_IF(DEBUG, " Uid breakdown:");
87
88 std::vector<int32_t> uids;
89 std::vector<int32_t> durationsPerUid;
90 for (auto& [uid, dur] : report.uidBreakdown) {
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +000091 uids.push_back(uid.val());
Prabir Pradhan44293792023-05-08 19:37:44 +000092 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
93 durationsPerUid.push_back(durMillis);
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +000094 ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
95 durMillis);
Prabir Pradhan44293792023-05-08 19:37:44 +000096 }
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +000097 util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
98 linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
99 sources, durationsPerSource, uids, durationsPerUid);
Prabir Pradhan852db892023-04-06 22:16:44 +0000100 }
101} sStatsdLogger;
102
103bool isIgnoredInputDeviceId(int32_t deviceId) {
104 switch (deviceId) {
105 case INVALID_INPUT_DEVICE_ID:
106 case VIRTUAL_KEYBOARD_ID:
107 return true;
108 default:
109 return false;
110 }
111}
112
113} // namespace
114
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000115InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
Prabir Pradhan852db892023-04-06 22:16:44 +0000116 : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
117
118InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
119 InputDeviceMetricsLogger& logger,
120 nanoseconds usageSessionTimeout)
Prabir Pradhan047695b2023-06-30 01:48:45 +0000121 : mNextListener(listener),
122 mLogger(logger),
123 mUsageSessionTimeout(usageSessionTimeout),
124 mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000125
126void InputDeviceMetricsCollector::notifyInputDevicesChanged(
127 const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000128 reportCompletedSessions();
Prabir Pradhan852db892023-04-06 22:16:44 +0000129 onInputDevicesChanged(args.inputDeviceInfos);
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000130 mNextListener.notify(args);
131}
132
133void InputDeviceMetricsCollector::notifyConfigurationChanged(
134 const NotifyConfigurationChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000135 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000136 mNextListener.notify(args);
137}
138
139void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000140 reportCompletedSessions();
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000141 const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
142 return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
Prabir Pradhanaff13032023-04-07 22:25:03 +0000143 };
144 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
Prabir Pradhan852db892023-04-06 22:16:44 +0000145
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000146 mNextListener.notify(args);
147}
148
149void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000150 reportCompletedSessions();
151 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
152 [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
Prabir Pradhan852db892023-04-06 22:16:44 +0000153
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000154 mNextListener.notify(args);
155}
156
157void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000158 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000159 mNextListener.notify(args);
160}
161
162void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000163 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000164 mNextListener.notify(args);
165}
166
167void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000168 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000169 mNextListener.notify(args);
170}
171
172void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000173 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000174 mNextListener.notify(args);
175}
176
177void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
178 const NotifyPointerCaptureChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000179 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000180 mNextListener.notify(args);
181}
182
Prabir Pradhan8ede1d12023-05-08 19:37:44 +0000183void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +0000184 const std::set<Uid>& uids) {
Prabir Pradhan047695b2023-06-30 01:48:45 +0000185 if (isIgnoredInputDeviceId(deviceId)) {
186 return;
187 }
Prabir Pradhan8a5c41d2023-06-08 19:13:46 +0000188 mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
Prabir Pradhan8ede1d12023-05-08 19:37:44 +0000189}
190
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000191void InputDeviceMetricsCollector::dump(std::string& dump) {
192 dump += "InputDeviceMetricsCollector:\n";
Prabir Pradhan852db892023-04-06 22:16:44 +0000193
194 dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
195 dump += " Devices with active usage sessions: " +
196 dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
197}
198
199void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000200 std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
Prabir Pradhan852db892023-04-06 22:16:44 +0000201
202 for (const InputDeviceInfo& info : infos) {
203 if (isIgnoredInputDeviceId(info.getId())) {
204 continue;
205 }
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000206 const auto& i = info.getIdentifier();
207 newDeviceInfos.emplace(info.getId(),
208 MetricsDeviceInfo{
209 .deviceId = info.getId(),
210 .vendor = i.vendor,
211 .product = i.product,
212 .version = i.version,
213 .bus = i.bus,
214 .isUsiStylus = info.getUsiVersion().has_value(),
215 .keyboardType = info.getKeyboardType(),
216 });
Prabir Pradhan852db892023-04-06 22:16:44 +0000217 }
218
Prabir Pradhanaff13032023-04-07 22:25:03 +0000219 for (auto [deviceId, info] : mLoggedDeviceInfos) {
220 if (newDeviceInfos.count(deviceId) != 0) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000221 continue;
222 }
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000223 onInputDeviceRemoved(deviceId, info);
Prabir Pradhan852db892023-04-06 22:16:44 +0000224 }
225
Prabir Pradhanaff13032023-04-07 22:25:03 +0000226 std::swap(newDeviceInfos, mLoggedDeviceInfos);
Prabir Pradhan852db892023-04-06 22:16:44 +0000227}
228
229void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
Prabir Pradhan0dd48ae2023-09-08 21:33:51 +0000230 const MetricsDeviceInfo& info) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000231 auto it = mActiveUsageSessions.find(deviceId);
Prabir Pradhanaff13032023-04-07 22:25:03 +0000232 if (it == mActiveUsageSessions.end()) {
233 return;
Prabir Pradhan852db892023-04-06 22:16:44 +0000234 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000235 // Report usage for that device if there is an active session.
236 auto& [_, activeSession] = *it;
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000237 mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
Prabir Pradhanaff13032023-04-07 22:25:03 +0000238 mActiveUsageSessions.erase(it);
239
Prabir Pradhan852db892023-04-06 22:16:44 +0000240 // We don't remove this from mLoggedDeviceInfos because it will be updated in
241 // onInputDevicesChanged().
242}
243
Prabir Pradhanaff13032023-04-07 22:25:03 +0000244void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
245 const SourceProvider& getSources) {
246 auto infoIt = mLoggedDeviceInfos.find(deviceId);
247 if (infoIt == mLoggedDeviceInfos.end()) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000248 // Do not track usage for devices that are not logged.
249 return;
250 }
251
Prabir Pradhanaff13032023-04-07 22:25:03 +0000252 auto [sessionIt, _] =
253 mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
254 for (InputDeviceUsageSource source : getSources(infoIt->second)) {
255 sessionIt->second.recordUsage(eventTime, source);
Prabir Pradhan852db892023-04-06 22:16:44 +0000256 }
257}
258
Prabir Pradhan44293792023-05-08 19:37:44 +0000259void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
260 auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
261 if (activeSessionIt == mActiveUsageSessions.end()) {
262 return;
263 }
Prabir Pradhan852db892023-04-06 22:16:44 +0000264
Prabir Pradhan44293792023-05-08 19:37:44 +0000265 activeSessionIt->second.recordInteraction(interaction);
266}
267
268void InputDeviceMetricsCollector::reportCompletedSessions() {
269 // Process all pending interactions.
270 for (auto interaction = mInteractionsQueue.pop(); interaction;
271 interaction = mInteractionsQueue.pop()) {
272 onInputDeviceInteraction(*interaction);
273 }
274
275 const auto currentTime = mLogger.getCurrentTime();
Prabir Pradhan852db892023-04-06 22:16:44 +0000276 std::vector<DeviceId> completedUsageSessions;
277
Prabir Pradhan44293792023-05-08 19:37:44 +0000278 // Process usages for all active session to determine if any sessions have expired.
Prabir Pradhanaff13032023-04-07 22:25:03 +0000279 for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
280 if (activeSession.checkIfCompletedAt(currentTime)) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000281 completedUsageSessions.emplace_back(deviceId);
282 }
283 }
284
Prabir Pradhan44293792023-05-08 19:37:44 +0000285 // Close out and log all expired usage sessions.
Prabir Pradhan852db892023-04-06 22:16:44 +0000286 for (DeviceId deviceId : completedUsageSessions) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000287 const auto infoIt = mLoggedDeviceInfos.find(deviceId);
288 LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
Prabir Pradhan852db892023-04-06 22:16:44 +0000289
Prabir Pradhanaff13032023-04-07 22:25:03 +0000290 auto activeSessionIt = mActiveUsageSessions.find(deviceId);
291 LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
292 auto& [_, activeSession] = *activeSessionIt;
Prabir Pradhan67d09ca2023-09-08 20:28:55 +0000293 mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
Prabir Pradhanaff13032023-04-07 22:25:03 +0000294 mActiveUsageSessions.erase(activeSessionIt);
Prabir Pradhan852db892023-04-06 22:16:44 +0000295 }
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000296}
297
Prabir Pradhanaff13032023-04-07 22:25:03 +0000298// --- InputDeviceMetricsCollector::ActiveSession ---
299
300InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
301 nanoseconds startTime)
302 : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
303
304void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
305 InputDeviceUsageSource source) {
306 // We assume that event times for subsequent events are always monotonically increasing for each
307 // input device.
308 auto [activeSourceIt, inserted] =
309 mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
310 if (!inserted) {
311 activeSourceIt->second.end = eventTime;
312 }
313 mDeviceSession.end = eventTime;
314}
315
Prabir Pradhan44293792023-05-08 19:37:44 +0000316void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
317 const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
318 const auto timestamp = std::get<nanoseconds>(interaction);
319 if (timestamp >= sessionExpiryTime) {
320 // This interaction occurred after the device's current active session is set to expire.
321 // Ignore it.
322 return;
323 }
324
325 for (Uid uid : std::get<std::set<Uid>>(interaction)) {
326 auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
327 if (!inserted) {
328 activeUidIt->second.end = timestamp;
329 }
330 }
331}
332
Prabir Pradhanaff13032023-04-07 22:25:03 +0000333bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
334 const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
335 std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
336 for (auto& [source, session] : mActiveSessionsBySource) {
337 if (session.end <= sessionExpiryTime) {
338 completedSourceSessionsForDevice.emplace_back(source);
339 }
340 }
341 for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
342 auto it = mActiveSessionsBySource.find(source);
343 const auto& [_, session] = *it;
344 mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
345 mActiveSessionsBySource.erase(it);
346 }
Prabir Pradhan44293792023-05-08 19:37:44 +0000347
348 std::vector<Uid> completedUidSessionsForDevice;
349 for (auto& [uid, session] : mActiveSessionsByUid) {
350 if (session.end <= sessionExpiryTime) {
351 completedUidSessionsForDevice.emplace_back(uid);
352 }
353 }
354 for (Uid uid : completedUidSessionsForDevice) {
355 auto it = mActiveSessionsByUid.find(uid);
356 const auto& [_, session] = *it;
357 mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
358 mActiveSessionsByUid.erase(it);
359 }
360
361 // This active session has expired if there are no more active source sessions tracked.
Prabir Pradhanaff13032023-04-07 22:25:03 +0000362 return mActiveSessionsBySource.empty();
363}
364
365InputDeviceMetricsLogger::DeviceUsageReport
366InputDeviceMetricsCollector::ActiveSession::finishSession() {
367 const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
368
369 for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
370 mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
371 }
372 mActiveSessionsBySource.clear();
373
Prabir Pradhan44293792023-05-08 19:37:44 +0000374 for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
375 mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
376 }
377 mActiveSessionsByUid.clear();
378
379 return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
Prabir Pradhanaff13032023-04-07 22:25:03 +0000380}
381
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000382} // namespace android