blob: 50f336b952f0aecd73cfa8c29e6c2ae016a032a1 [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 Pradhan852db892023-04-06 22:16:44 +000020#include <android-base/stringprintf.h>
21#include <input/PrintTools.h>
22#include <linux/input.h>
23#include <statslog.h>
24
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;
29
30namespace {
31
32constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::seconds(5);
33
34/**
35 * Log debug messages about metrics events logged to statsd.
36 * Enable this via "adb shell setprop log.tag.InputDeviceMetricsCollector DEBUG" (requires restart)
37 */
38const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
39
40int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
41 switch (linuxBus) {
42 case BUS_USB:
43 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
44 case BUS_BLUETOOTH:
45 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
46 default:
47 return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
48 }
49}
50
51class : public InputDeviceMetricsLogger {
52 nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
53
54 void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
55 nanoseconds sessionDuration) override {
56 const int32_t durationMillis =
57 std::chrono::duration_cast<std::chrono::milliseconds>(sessionDuration).count();
58 const static std::vector<int32_t> empty;
59
60 ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
61 ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
62
63 util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
64 identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
65 durationMillis, /*usage_sources=*/empty,
66 /*usage_durations_per_source=*/empty, /*uids=*/empty,
67 /*usage_durations_per_uid=*/empty);
68 }
69} sStatsdLogger;
70
71bool isIgnoredInputDeviceId(int32_t deviceId) {
72 switch (deviceId) {
73 case INVALID_INPUT_DEVICE_ID:
74 case VIRTUAL_KEYBOARD_ID:
75 return true;
76 default:
77 return false;
78 }
79}
80
81} // namespace
82
Prabir Pradhanaddf8e92023-04-06 00:28:48 +000083InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
Prabir Pradhan852db892023-04-06 22:16:44 +000084 : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
85
86InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
87 InputDeviceMetricsLogger& logger,
88 nanoseconds usageSessionTimeout)
89 : mNextListener(listener), mLogger(logger), mUsageSessionTimeout(usageSessionTimeout) {}
Prabir Pradhanaddf8e92023-04-06 00:28:48 +000090
91void InputDeviceMetricsCollector::notifyInputDevicesChanged(
92 const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +000093 processUsages();
94 onInputDevicesChanged(args.inputDeviceInfos);
Prabir Pradhanaddf8e92023-04-06 00:28:48 +000095 mNextListener.notify(args);
96}
97
98void InputDeviceMetricsCollector::notifyConfigurationChanged(
99 const NotifyConfigurationChangedArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000100 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000101 mNextListener.notify(args);
102}
103
104void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000105 processUsages();
106 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime));
107
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000108 mNextListener.notify(args);
109}
110
111void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000112 processUsages();
113 onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime));
114
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000115 mNextListener.notify(args);
116}
117
118void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000119 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000120 mNextListener.notify(args);
121}
122
123void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000124 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000125 mNextListener.notify(args);
126}
127
128void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000129 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000130 mNextListener.notify(args);
131}
132
133void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000134 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000135 mNextListener.notify(args);
136}
137
138void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
139 const NotifyPointerCaptureChangedArgs& args) {
Prabir Pradhan852db892023-04-06 22:16:44 +0000140 processUsages();
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000141 mNextListener.notify(args);
142}
143
144void InputDeviceMetricsCollector::dump(std::string& dump) {
145 dump += "InputDeviceMetricsCollector:\n";
Prabir Pradhan852db892023-04-06 22:16:44 +0000146
147 dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
148 dump += " Devices with active usage sessions: " +
149 dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
150}
151
152void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
153 std::map<DeviceId, InputDeviceIdentifier> newDeviceIds;
154
155 for (const InputDeviceInfo& info : infos) {
156 if (isIgnoredInputDeviceId(info.getId())) {
157 continue;
158 }
159 newDeviceIds.emplace(info.getId(), info.getIdentifier());
160 }
161
162 for (auto [deviceId, identifier] : mLoggedDeviceInfos) {
163 if (newDeviceIds.count(deviceId) != 0) {
164 continue;
165 }
166 onInputDeviceRemoved(deviceId, identifier);
167 }
168
169 std::swap(newDeviceIds, mLoggedDeviceInfos);
170}
171
172void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
173 const InputDeviceIdentifier& identifier) {
174 // Report usage for that device if there is an active session.
175 auto it = mActiveUsageSessions.find(deviceId);
176 if (it != mActiveUsageSessions.end()) {
177 mLogger.logInputDeviceUsageReported(identifier, it->second.end - it->second.start);
178 mActiveUsageSessions.erase(it);
179 }
180 // We don't remove this from mLoggedDeviceInfos because it will be updated in
181 // onInputDevicesChanged().
182}
183
184void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime) {
185 if (mLoggedDeviceInfos.count(deviceId) == 0) {
186 // Do not track usage for devices that are not logged.
187 return;
188 }
189
190 auto [it, inserted] = mActiveUsageSessions.try_emplace(deviceId, eventTime, eventTime);
191 if (!inserted) {
192 it->second.end = eventTime;
193 }
194}
195
196void InputDeviceMetricsCollector::processUsages() {
197 const auto usageSessionExpiryTime = mLogger.getCurrentTime() - mUsageSessionTimeout;
198
199 std::vector<DeviceId> completedUsageSessions;
200
201 for (const auto& [deviceId, usageSession] : mActiveUsageSessions) {
202 if (usageSession.end <= usageSessionExpiryTime) {
203 completedUsageSessions.emplace_back(deviceId);
204 }
205 }
206
207 for (DeviceId deviceId : completedUsageSessions) {
208 const auto it = mLoggedDeviceInfos.find(deviceId);
209 LOG_ALWAYS_FATAL_IF(it == mLoggedDeviceInfos.end());
210
211 const auto& session = mActiveUsageSessions[deviceId];
212 mLogger.logInputDeviceUsageReported(it->second, session.end - session.start);
213
214 mActiveUsageSessions.erase(deviceId);
215 }
Prabir Pradhanaddf8e92023-04-06 00:28:48 +0000216}
217
218} // namespace android