blob: c21a4f236d8e2df4d2ac0b5d87e5a2e24550245e [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;
30
31namespace {
32
33constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::seconds(5);
34
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
41int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
Harry Cuttsa34de522023-06-06 15:52:54 +000042 // When adding cases to this switch, also add them to the copy of this method in
43 // TouchpadInputMapper.cpp.
44 // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
Prabir Pradhan852db892023-04-06 22:16:44 +000045 switch (linuxBus) {
46 case BUS_USB:
47 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
48 case BUS_BLUETOOTH:
49 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
50 default:
51 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
52 }
53}
54
55class : public InputDeviceMetricsLogger {
56 nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
57
58 void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
Prabir Pradhanaff13032023-04-07 22:25:03 +000059 const DeviceUsageReport& report) override {
Prabir Pradhan852db892023-04-06 22:16:44 +000060 const int32_t durationMillis =
Prabir Pradhanaff13032023-04-07 22:25:03 +000061 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
Prabir Pradhan852db892023-04-06 22:16:44 +000062 const static std::vector<int32_t> empty;
63
64 ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
65 ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
Prabir Pradhanaff13032023-04-07 22:25:03 +000066 ALOGD_IF(DEBUG, " Source breakdown:");
67
68 std::vector<int32_t> sources;
69 std::vector<int32_t> durationsPerSource;
70 for (auto& [src, dur] : report.sourceBreakdown) {
71 sources.push_back(ftl::to_underlying(src));
72 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
73 durationsPerSource.emplace_back(durMillis);
74 ALOGD_IF(DEBUG, " - usageSource: %s\t duration: %dms",
75 ftl::enum_string(src).c_str(), durMillis);
76 }
Prabir Pradhan852db892023-04-06 22:16:44 +000077
78 util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
79 identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
Prabir Pradhanaff13032023-04-07 22:25:03 +000080 durationMillis, sources, durationsPerSource, /*uids=*/empty,
Prabir Pradhan852db892023-04-06 22:16:44 +000081 /*usage_durations_per_uid=*/empty);
82 }
83} sStatsdLogger;
84
85bool isIgnoredInputDeviceId(int32_t deviceId) {
86 switch (deviceId) {
87 case INVALID_INPUT_DEVICE_ID:
88 case VIRTUAL_KEYBOARD_ID:
89 return true;
90 default:
91 return false;
92 }
93}
94
95} // namespace
96
Prabir Pradhanae10ee62023-05-12 19:44:18 +000097InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo& info,
98 const NotifyKeyArgs& keyArgs) {
99 if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
100 return InputDeviceUsageSource::UNKNOWN;
101 }
102
103 if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
104 DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
105 return InputDeviceUsageSource::DPAD;
106 }
107
108 if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
109 GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
110 return InputDeviceUsageSource::GAMEPAD;
111 }
112
113 if (info.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
114 return InputDeviceUsageSource::KEYBOARD;
115 }
116
117 return InputDeviceUsageSource::BUTTONS;
118}
119
120std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
121 LOG_ALWAYS_FATAL_IF(motionArgs.pointerCount < 1, "Received motion args without pointers");
122 std::set<InputDeviceUsageSource> sources;
123
124 for (uint32_t i = 0; i < motionArgs.pointerCount; i++) {
125 const auto toolType = motionArgs.pointerProperties[i].toolType;
126 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
127 if (toolType == ToolType::MOUSE) {
128 sources.emplace(InputDeviceUsageSource::MOUSE);
129 continue;
130 }
131 if (toolType == ToolType::FINGER) {
132 sources.emplace(InputDeviceUsageSource::TOUCHPAD);
133 continue;
134 }
135 if (isStylusToolType(toolType)) {
136 sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
137 continue;
138 }
139 }
140 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
141 toolType == ToolType::MOUSE) {
142 sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
143 continue;
144 }
145 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
146 toolType == ToolType::FINGER) {
147 sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
148 continue;
149 }
150 if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
151 isStylusToolType(toolType)) {
152 sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
153 continue;
154 }
155 if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
156 sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
157 continue;
158 }
159 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
160 sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
161 continue;
162 }
163 if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
164 sources.emplace(InputDeviceUsageSource::JOYSTICK);
165 continue;
166 }
167 if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
168 sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
169 continue;
170 }
171 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
172 sources.emplace(InputDeviceUsageSource::TRACKBALL);
173 continue;
174 }
175 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
176 sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
177 continue;
178 }
179 sources.emplace(InputDeviceUsageSource::UNKNOWN);
180 }
181
182 return sources;
183}
184
185// --- InputDeviceMetricsCollector ---
186
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000187InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
Prabir Pradhan852db892023-04-06 22:16:44 +0000188 : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
189
190InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
191 InputDeviceMetricsLogger& logger,
192 nanoseconds usageSessionTimeout)
193 : mNextListener(listener), mLogger(logger), mUsageSessionTimeout(usageSessionTimeout) {}
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000194
195void InputDeviceMetricsCollector::notifyInputDevicesChanged(
196 const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000197 reportCompletedSessions();
Prabir Pradhan852db892023-04-06 22:16:44 +0000198 onInputDevicesChanged(args.inputDeviceInfos);
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000199 mNextListener.notify(args);
200}
201
202void InputDeviceMetricsCollector::notifyConfigurationChanged(
203 const NotifyConfigurationChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000204 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000205 mNextListener.notify(args);
206}
207
208void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000209 reportCompletedSessions();
210 const SourceProvider getSources = [&args](const InputDeviceInfo& info) {
211 return std::set{getUsageSourceForKeyArgs(info, args)};
212 };
213 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
Prabir Pradhan852db892023-04-06 22:16:44 +0000214
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000215 mNextListener.notify(args);
216}
217
218void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000219 reportCompletedSessions();
220 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
221 [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
Prabir Pradhan852db892023-04-06 22:16:44 +0000222
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000223 mNextListener.notify(args);
224}
225
226void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000227 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000228 mNextListener.notify(args);
229}
230
231void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000232 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000233 mNextListener.notify(args);
234}
235
236void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000237 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000238 mNextListener.notify(args);
239}
240
241void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000242 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000243 mNextListener.notify(args);
244}
245
246void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
247 const NotifyPointerCaptureChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000248 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000249 mNextListener.notify(args);
250}
251
252void InputDeviceMetricsCollector::dump(std::string& dump) {
253 dump += "InputDeviceMetricsCollector:\n";
Prabir Pradhan852db892023-04-06 22:16:44 +0000254
255 dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
256 dump += " Devices with active usage sessions: " +
257 dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
258}
259
260void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000261 std::map<DeviceId, InputDeviceInfo> newDeviceInfos;
Prabir Pradhan852db892023-04-06 22:16:44 +0000262
263 for (const InputDeviceInfo& info : infos) {
264 if (isIgnoredInputDeviceId(info.getId())) {
265 continue;
266 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000267 newDeviceInfos.emplace(info.getId(), info);
Prabir Pradhan852db892023-04-06 22:16:44 +0000268 }
269
Prabir Pradhanaff13032023-04-07 22:25:03 +0000270 for (auto [deviceId, info] : mLoggedDeviceInfos) {
271 if (newDeviceInfos.count(deviceId) != 0) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000272 continue;
273 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000274 onInputDeviceRemoved(deviceId, info.getIdentifier());
Prabir Pradhan852db892023-04-06 22:16:44 +0000275 }
276
Prabir Pradhanaff13032023-04-07 22:25:03 +0000277 std::swap(newDeviceInfos, mLoggedDeviceInfos);
Prabir Pradhan852db892023-04-06 22:16:44 +0000278}
279
280void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
281 const InputDeviceIdentifier& identifier) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000282 auto it = mActiveUsageSessions.find(deviceId);
Prabir Pradhanaff13032023-04-07 22:25:03 +0000283 if (it == mActiveUsageSessions.end()) {
284 return;
Prabir Pradhan852db892023-04-06 22:16:44 +0000285 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000286 // Report usage for that device if there is an active session.
287 auto& [_, activeSession] = *it;
288 mLogger.logInputDeviceUsageReported(identifier, activeSession.finishSession());
289 mActiveUsageSessions.erase(it);
290
Prabir Pradhan852db892023-04-06 22:16:44 +0000291 // We don't remove this from mLoggedDeviceInfos because it will be updated in
292 // onInputDevicesChanged().
293}
294
Prabir Pradhanaff13032023-04-07 22:25:03 +0000295void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
296 const SourceProvider& getSources) {
297 auto infoIt = mLoggedDeviceInfos.find(deviceId);
298 if (infoIt == mLoggedDeviceInfos.end()) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000299 // Do not track usage for devices that are not logged.
300 return;
301 }
302
Prabir Pradhanaff13032023-04-07 22:25:03 +0000303 auto [sessionIt, _] =
304 mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
305 for (InputDeviceUsageSource source : getSources(infoIt->second)) {
306 sessionIt->second.recordUsage(eventTime, source);
Prabir Pradhan852db892023-04-06 22:16:44 +0000307 }
308}
309
Prabir Pradhanaff13032023-04-07 22:25:03 +0000310void InputDeviceMetricsCollector::reportCompletedSessions() {
311 const auto currentTime = mLogger.getCurrentTime();
Prabir Pradhan852db892023-04-06 22:16:44 +0000312
313 std::vector<DeviceId> completedUsageSessions;
314
Prabir Pradhanaff13032023-04-07 22:25:03 +0000315 for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
316 if (activeSession.checkIfCompletedAt(currentTime)) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000317 completedUsageSessions.emplace_back(deviceId);
318 }
319 }
320
321 for (DeviceId deviceId : completedUsageSessions) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000322 const auto infoIt = mLoggedDeviceInfos.find(deviceId);
323 LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
Prabir Pradhan852db892023-04-06 22:16:44 +0000324
Prabir Pradhanaff13032023-04-07 22:25:03 +0000325 auto activeSessionIt = mActiveUsageSessions.find(deviceId);
326 LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
327 auto& [_, activeSession] = *activeSessionIt;
328 mLogger.logInputDeviceUsageReported(infoIt->second.getIdentifier(),
329 activeSession.finishSession());
330 mActiveUsageSessions.erase(activeSessionIt);
Prabir Pradhan852db892023-04-06 22:16:44 +0000331 }
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000332}
333
Prabir Pradhanaff13032023-04-07 22:25:03 +0000334// --- InputDeviceMetricsCollector::ActiveSession ---
335
336InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
337 nanoseconds startTime)
338 : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
339
340void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
341 InputDeviceUsageSource source) {
342 // We assume that event times for subsequent events are always monotonically increasing for each
343 // input device.
344 auto [activeSourceIt, inserted] =
345 mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
346 if (!inserted) {
347 activeSourceIt->second.end = eventTime;
348 }
349 mDeviceSession.end = eventTime;
350}
351
352bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
353 const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
354 std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
355 for (auto& [source, session] : mActiveSessionsBySource) {
356 if (session.end <= sessionExpiryTime) {
357 completedSourceSessionsForDevice.emplace_back(source);
358 }
359 }
360 for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
361 auto it = mActiveSessionsBySource.find(source);
362 const auto& [_, session] = *it;
363 mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
364 mActiveSessionsBySource.erase(it);
365 }
366 return mActiveSessionsBySource.empty();
367}
368
369InputDeviceMetricsLogger::DeviceUsageReport
370InputDeviceMetricsCollector::ActiveSession::finishSession() {
371 const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
372
373 for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
374 mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
375 }
376 mActiveSessionsBySource.clear();
377
378 return {deviceUsageDuration, mSourceUsageBreakdown};
379}
380
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000381} // namespace android