blob: 397b1cd7c2556f7dc36cf96eec2e64b5673e8c0c [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) {
42 switch (linuxBus) {
43 case BUS_USB:
44 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
45 case BUS_BLUETOOTH:
46 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
47 default:
48 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
49 }
50}
51
52class : public InputDeviceMetricsLogger {
53 nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
54
55 void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
Prabir Pradhanaff13032023-04-07 22:25:03 +000056 const DeviceUsageReport& report) override {
Prabir Pradhan852db892023-04-06 22:16:44 +000057 const int32_t durationMillis =
Prabir Pradhanaff13032023-04-07 22:25:03 +000058 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
Prabir Pradhan852db892023-04-06 22:16:44 +000059 const static std::vector<int32_t> empty;
60
61 ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
62 ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
Prabir Pradhanaff13032023-04-07 22:25:03 +000063 ALOGD_IF(DEBUG, " Source breakdown:");
64
65 std::vector<int32_t> sources;
66 std::vector<int32_t> durationsPerSource;
67 for (auto& [src, dur] : report.sourceBreakdown) {
68 sources.push_back(ftl::to_underlying(src));
69 int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
70 durationsPerSource.emplace_back(durMillis);
71 ALOGD_IF(DEBUG, " - usageSource: %s\t duration: %dms",
72 ftl::enum_string(src).c_str(), durMillis);
73 }
Prabir Pradhan852db892023-04-06 22:16:44 +000074
75 util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
76 identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
Prabir Pradhanaff13032023-04-07 22:25:03 +000077 durationMillis, sources, durationsPerSource, /*uids=*/empty,
Prabir Pradhan852db892023-04-06 22:16:44 +000078 /*usage_durations_per_uid=*/empty);
79 }
80} sStatsdLogger;
81
82bool isIgnoredInputDeviceId(int32_t deviceId) {
83 switch (deviceId) {
84 case INVALID_INPUT_DEVICE_ID:
85 case VIRTUAL_KEYBOARD_ID:
86 return true;
87 default:
88 return false;
89 }
90}
91
92} // namespace
93
Prabir Pradhanae10ee62023-05-12 19:44:18 +000094InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo& info,
95 const NotifyKeyArgs& keyArgs) {
96 if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
97 return InputDeviceUsageSource::UNKNOWN;
98 }
99
100 if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
101 DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
102 return InputDeviceUsageSource::DPAD;
103 }
104
105 if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
106 GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
107 return InputDeviceUsageSource::GAMEPAD;
108 }
109
110 if (info.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
111 return InputDeviceUsageSource::KEYBOARD;
112 }
113
114 return InputDeviceUsageSource::BUTTONS;
115}
116
117std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
118 LOG_ALWAYS_FATAL_IF(motionArgs.pointerCount < 1, "Received motion args without pointers");
119 std::set<InputDeviceUsageSource> sources;
120
121 for (uint32_t i = 0; i < motionArgs.pointerCount; i++) {
122 const auto toolType = motionArgs.pointerProperties[i].toolType;
123 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
124 if (toolType == ToolType::MOUSE) {
125 sources.emplace(InputDeviceUsageSource::MOUSE);
126 continue;
127 }
128 if (toolType == ToolType::FINGER) {
129 sources.emplace(InputDeviceUsageSource::TOUCHPAD);
130 continue;
131 }
132 if (isStylusToolType(toolType)) {
133 sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
134 continue;
135 }
136 }
137 if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
138 toolType == ToolType::MOUSE) {
139 sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
140 continue;
141 }
142 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
143 toolType == ToolType::FINGER) {
144 sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
145 continue;
146 }
147 if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
148 isStylusToolType(toolType)) {
149 sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
150 continue;
151 }
152 if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
153 sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
154 continue;
155 }
156 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
157 sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
158 continue;
159 }
160 if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
161 sources.emplace(InputDeviceUsageSource::JOYSTICK);
162 continue;
163 }
164 if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
165 sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
166 continue;
167 }
168 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
169 sources.emplace(InputDeviceUsageSource::TRACKBALL);
170 continue;
171 }
172 if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
173 sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
174 continue;
175 }
176 sources.emplace(InputDeviceUsageSource::UNKNOWN);
177 }
178
179 return sources;
180}
181
182// --- InputDeviceMetricsCollector ---
183
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000184InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
Prabir Pradhan852db892023-04-06 22:16:44 +0000185 : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
186
187InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
188 InputDeviceMetricsLogger& logger,
189 nanoseconds usageSessionTimeout)
190 : mNextListener(listener), mLogger(logger), mUsageSessionTimeout(usageSessionTimeout) {}
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000191
192void InputDeviceMetricsCollector::notifyInputDevicesChanged(
193 const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000194 reportCompletedSessions();
Prabir Pradhan852db892023-04-06 22:16:44 +0000195 onInputDevicesChanged(args.inputDeviceInfos);
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000196 mNextListener.notify(args);
197}
198
199void InputDeviceMetricsCollector::notifyConfigurationChanged(
200 const NotifyConfigurationChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000201 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000202 mNextListener.notify(args);
203}
204
205void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000206 reportCompletedSessions();
207 const SourceProvider getSources = [&args](const InputDeviceInfo& info) {
208 return std::set{getUsageSourceForKeyArgs(info, args)};
209 };
210 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
Prabir Pradhan852db892023-04-06 22:16:44 +0000211
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000212 mNextListener.notify(args);
213}
214
215void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000216 reportCompletedSessions();
217 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
218 [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
Prabir Pradhan852db892023-04-06 22:16:44 +0000219
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000220 mNextListener.notify(args);
221}
222
223void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000224 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000225 mNextListener.notify(args);
226}
227
228void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000229 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000230 mNextListener.notify(args);
231}
232
233void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000234 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000235 mNextListener.notify(args);
236}
237
238void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000239 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000240 mNextListener.notify(args);
241}
242
243void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
244 const NotifyPointerCaptureChangedArgs& args) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000245 reportCompletedSessions();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000246 mNextListener.notify(args);
247}
248
Prabir Pradhan8ede1d12023-05-08 19:37:44 +0000249void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
250 const std::set<int32_t>& uids) {
251 // TODO: Implement.
252}
253
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000254void InputDeviceMetricsCollector::dump(std::string& dump) {
255 dump += "InputDeviceMetricsCollector:\n";
Prabir Pradhan852db892023-04-06 22:16:44 +0000256
257 dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
258 dump += " Devices with active usage sessions: " +
259 dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
260}
261
262void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000263 std::map<DeviceId, InputDeviceInfo> newDeviceInfos;
Prabir Pradhan852db892023-04-06 22:16:44 +0000264
265 for (const InputDeviceInfo& info : infos) {
266 if (isIgnoredInputDeviceId(info.getId())) {
267 continue;
268 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000269 newDeviceInfos.emplace(info.getId(), info);
Prabir Pradhan852db892023-04-06 22:16:44 +0000270 }
271
Prabir Pradhanaff13032023-04-07 22:25:03 +0000272 for (auto [deviceId, info] : mLoggedDeviceInfos) {
273 if (newDeviceInfos.count(deviceId) != 0) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000274 continue;
275 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000276 onInputDeviceRemoved(deviceId, info.getIdentifier());
Prabir Pradhan852db892023-04-06 22:16:44 +0000277 }
278
Prabir Pradhanaff13032023-04-07 22:25:03 +0000279 std::swap(newDeviceInfos, mLoggedDeviceInfos);
Prabir Pradhan852db892023-04-06 22:16:44 +0000280}
281
282void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
283 const InputDeviceIdentifier& identifier) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000284 auto it = mActiveUsageSessions.find(deviceId);
Prabir Pradhanaff13032023-04-07 22:25:03 +0000285 if (it == mActiveUsageSessions.end()) {
286 return;
Prabir Pradhan852db892023-04-06 22:16:44 +0000287 }
Prabir Pradhanaff13032023-04-07 22:25:03 +0000288 // Report usage for that device if there is an active session.
289 auto& [_, activeSession] = *it;
290 mLogger.logInputDeviceUsageReported(identifier, activeSession.finishSession());
291 mActiveUsageSessions.erase(it);
292
Prabir Pradhan852db892023-04-06 22:16:44 +0000293 // We don't remove this from mLoggedDeviceInfos because it will be updated in
294 // onInputDevicesChanged().
295}
296
Prabir Pradhanaff13032023-04-07 22:25:03 +0000297void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
298 const SourceProvider& getSources) {
299 auto infoIt = mLoggedDeviceInfos.find(deviceId);
300 if (infoIt == mLoggedDeviceInfos.end()) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000301 // Do not track usage for devices that are not logged.
302 return;
303 }
304
Prabir Pradhanaff13032023-04-07 22:25:03 +0000305 auto [sessionIt, _] =
306 mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
307 for (InputDeviceUsageSource source : getSources(infoIt->second)) {
308 sessionIt->second.recordUsage(eventTime, source);
Prabir Pradhan852db892023-04-06 22:16:44 +0000309 }
310}
311
Prabir Pradhanaff13032023-04-07 22:25:03 +0000312void InputDeviceMetricsCollector::reportCompletedSessions() {
313 const auto currentTime = mLogger.getCurrentTime();
Prabir Pradhan852db892023-04-06 22:16:44 +0000314
315 std::vector<DeviceId> completedUsageSessions;
316
Prabir Pradhanaff13032023-04-07 22:25:03 +0000317 for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
318 if (activeSession.checkIfCompletedAt(currentTime)) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000319 completedUsageSessions.emplace_back(deviceId);
320 }
321 }
322
323 for (DeviceId deviceId : completedUsageSessions) {
Prabir Pradhanaff13032023-04-07 22:25:03 +0000324 const auto infoIt = mLoggedDeviceInfos.find(deviceId);
325 LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
Prabir Pradhan852db892023-04-06 22:16:44 +0000326
Prabir Pradhanaff13032023-04-07 22:25:03 +0000327 auto activeSessionIt = mActiveUsageSessions.find(deviceId);
328 LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
329 auto& [_, activeSession] = *activeSessionIt;
330 mLogger.logInputDeviceUsageReported(infoIt->second.getIdentifier(),
331 activeSession.finishSession());
332 mActiveUsageSessions.erase(activeSessionIt);
Prabir Pradhan852db892023-04-06 22:16:44 +0000333 }
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000334}
335
Prabir Pradhanaff13032023-04-07 22:25:03 +0000336// --- InputDeviceMetricsCollector::ActiveSession ---
337
338InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
339 nanoseconds startTime)
340 : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
341
342void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
343 InputDeviceUsageSource source) {
344 // We assume that event times for subsequent events are always monotonically increasing for each
345 // input device.
346 auto [activeSourceIt, inserted] =
347 mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
348 if (!inserted) {
349 activeSourceIt->second.end = eventTime;
350 }
351 mDeviceSession.end = eventTime;
352}
353
354bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
355 const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
356 std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
357 for (auto& [source, session] : mActiveSessionsBySource) {
358 if (session.end <= sessionExpiryTime) {
359 completedSourceSessionsForDevice.emplace_back(source);
360 }
361 }
362 for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
363 auto it = mActiveSessionsBySource.find(source);
364 const auto& [_, session] = *it;
365 mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
366 mActiveSessionsBySource.erase(it);
367 }
368 return mActiveSessionsBySource.empty();
369}
370
371InputDeviceMetricsLogger::DeviceUsageReport
372InputDeviceMetricsCollector::ActiveSession::finishSession() {
373 const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
374
375 for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
376 mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
377 }
378 mActiveSessionsBySource.clear();
379
380 return {deviceUsageDuration, mSourceUsageBreakdown};
381}
382
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000383} // namespace android