• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
20 #include "InputDeviceMetricsSource.h"
21 
22 #include <android-base/stringprintf.h>
23 #include <input/PrintTools.h>
24 
25 namespace android {
26 
27 using android::base::StringPrintf;
28 using std::chrono::nanoseconds;
29 using std::chrono_literals::operator""ns;
30 
31 namespace {
32 
33 constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
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  */
39 const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
40 
41 constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
42 
linuxBusToInputDeviceBusEnum(int32_t linuxBus,bool isUsiStylus)43 int32_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 
50     // 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.
53     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 
63 class : public InputDeviceMetricsLogger {
getCurrentTime()64     nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
65 
logInputDeviceUsageReported(const MetricsDeviceInfo & info,const DeviceUsageReport & report)66     void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
67                                      const DeviceUsageReport& report) override {
68         const int32_t durationMillis =
69                 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
70         const static std::vector<int32_t> empty;
71 
72         ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
73         ALOGD_IF(DEBUG, "    Total duration: %dms", durationMillis);
74         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         }
85 
86         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) {
91             uids.push_back(uid.val());
92             int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
93             durationsPerUid.push_back(durMillis);
94             ALOGD_IF(DEBUG, "        - uid: %s\t duration: %dms", uid.toString().c_str(),
95                      durMillis);
96         }
97         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);
100     }
101 } sStatsdLogger;
102 
isIgnoredInputDeviceId(int32_t deviceId)103 bool 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 
InputDeviceMetricsCollector(InputListenerInterface & listener)115 InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
116       : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
117 
InputDeviceMetricsCollector(InputListenerInterface & listener,InputDeviceMetricsLogger & logger,nanoseconds usageSessionTimeout)118 InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
119                                                          InputDeviceMetricsLogger& logger,
120                                                          nanoseconds usageSessionTimeout)
121       : mNextListener(listener),
122         mLogger(logger),
123         mUsageSessionTimeout(usageSessionTimeout),
124         mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
125 
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)126 void InputDeviceMetricsCollector::notifyInputDevicesChanged(
127         const NotifyInputDevicesChangedArgs& args) {
128     {
129         std::scoped_lock lock(mLock);
130         reportCompletedSessions();
131         onInputDevicesChanged(args.inputDeviceInfos);
132     }
133     mNextListener.notify(args);
134 }
135 
notifyConfigurationChanged(const NotifyConfigurationChangedArgs & args)136 void InputDeviceMetricsCollector::notifyConfigurationChanged(
137         const NotifyConfigurationChangedArgs& args) {
138     {
139         std::scoped_lock lock(mLock);
140         reportCompletedSessions();
141     }
142     mNextListener.notify(args);
143 }
144 
notifyKey(const NotifyKeyArgs & args)145 void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
146     {
147         std::scoped_lock lock(mLock);
148         reportCompletedSessions();
149         const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
150             return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
151         };
152         onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
153     }
154     mNextListener.notify(args);
155 }
156 
notifyMotion(const NotifyMotionArgs & args)157 void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
158     {
159         std::scoped_lock lock(mLock);
160         reportCompletedSessions();
161         onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
162                            [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
163     }
164 
165     mNextListener.notify(args);
166 }
167 
notifySwitch(const NotifySwitchArgs & args)168 void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
169     {
170         std::scoped_lock lock(mLock);
171         reportCompletedSessions();
172     }
173     mNextListener.notify(args);
174 }
175 
notifySensor(const NotifySensorArgs & args)176 void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
177     {
178         std::scoped_lock lock(mLock);
179         reportCompletedSessions();
180     }
181     mNextListener.notify(args);
182 }
183 
notifyVibratorState(const NotifyVibratorStateArgs & args)184 void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
185     {
186         std::scoped_lock lock(mLock);
187         reportCompletedSessions();
188     }
189     mNextListener.notify(args);
190 }
191 
notifyDeviceReset(const NotifyDeviceResetArgs & args)192 void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
193     {
194         std::scoped_lock lock(mLock);
195         reportCompletedSessions();
196     }
197     mNextListener.notify(args);
198 }
199 
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)200 void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
201         const NotifyPointerCaptureChangedArgs& args) {
202     {
203         std::scoped_lock lock(mLock);
204         reportCompletedSessions();
205     }
206     mNextListener.notify(args);
207 }
208 
notifyDeviceInteraction(int32_t deviceId,nsecs_t timestamp,const std::set<Uid> & uids)209 void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
210                                                           const std::set<Uid>& uids) {
211     if (isIgnoredInputDeviceId(deviceId)) {
212         return;
213     }
214     std::scoped_lock lock(mLock);
215     mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
216 }
217 
dump(std::string & dump)218 void InputDeviceMetricsCollector::dump(std::string& dump) {
219     std::scoped_lock lock(mLock);
220     dump += "InputDeviceMetricsCollector:\n";
221 
222     dump += "  Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
223     dump += "  Devices with active usage sessions: " +
224             dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
225 }
226 
monitor()227 void InputDeviceMetricsCollector::monitor() {
228     std::scoped_lock lock(mLock);
229 }
230 
onInputDevicesChanged(const std::vector<InputDeviceInfo> & infos)231 void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
232     std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
233 
234     for (const InputDeviceInfo& info : infos) {
235         if (isIgnoredInputDeviceId(info.getId())) {
236             continue;
237         }
238         const auto& i = info.getIdentifier();
239         newDeviceInfos.emplace(info.getId(),
240                                MetricsDeviceInfo{
241                                        .deviceId = info.getId(),
242                                        .vendor = i.vendor,
243                                        .product = i.product,
244                                        .version = i.version,
245                                        .bus = i.bus,
246                                        .isUsiStylus = info.getUsiVersion().has_value(),
247                                        .keyboardType = info.getKeyboardType(),
248                                });
249     }
250 
251     for (auto [deviceId, info] : mLoggedDeviceInfos) {
252         if (newDeviceInfos.count(deviceId) != 0) {
253             continue;
254         }
255         onInputDeviceRemoved(deviceId, info);
256     }
257 
258     std::swap(newDeviceInfos, mLoggedDeviceInfos);
259 }
260 
onInputDeviceRemoved(DeviceId deviceId,const MetricsDeviceInfo & info)261 void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
262                                                        const MetricsDeviceInfo& info) {
263     auto it = mActiveUsageSessions.find(deviceId);
264     if (it == mActiveUsageSessions.end()) {
265         return;
266     }
267     // Report usage for that device if there is an active session.
268     auto& [_, activeSession] = *it;
269     mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
270     mActiveUsageSessions.erase(it);
271 
272     // We don't remove this from mLoggedDeviceInfos because it will be updated in
273     // onInputDevicesChanged().
274 }
275 
onInputDeviceUsage(DeviceId deviceId,nanoseconds eventTime,const SourceProvider & getSources)276 void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
277                                                      const SourceProvider& getSources) {
278     auto infoIt = mLoggedDeviceInfos.find(deviceId);
279     if (infoIt == mLoggedDeviceInfos.end()) {
280         // Do not track usage for devices that are not logged.
281         return;
282     }
283 
284     auto [sessionIt, _] =
285             mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
286     for (InputDeviceUsageSource source : getSources(infoIt->second)) {
287         sessionIt->second.recordUsage(eventTime, source);
288     }
289 }
290 
onInputDeviceInteraction(const Interaction & interaction)291 void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
292     auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
293     if (activeSessionIt == mActiveUsageSessions.end()) {
294         return;
295     }
296 
297     activeSessionIt->second.recordInteraction(interaction);
298 }
299 
reportCompletedSessions()300 void InputDeviceMetricsCollector::reportCompletedSessions() {
301     // Process all pending interactions.
302     for (auto interaction = mInteractionsQueue.pop(); interaction;
303          interaction = mInteractionsQueue.pop()) {
304         onInputDeviceInteraction(*interaction);
305     }
306 
307     const auto currentTime = mLogger.getCurrentTime();
308     std::vector<DeviceId> completedUsageSessions;
309 
310     // Process usages for all active session to determine if any sessions have expired.
311     for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
312         if (activeSession.checkIfCompletedAt(currentTime)) {
313             completedUsageSessions.emplace_back(deviceId);
314         }
315     }
316 
317     // Close out and log all expired usage sessions.
318     for (DeviceId deviceId : completedUsageSessions) {
319         const auto infoIt = mLoggedDeviceInfos.find(deviceId);
320         LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
321 
322         auto activeSessionIt = mActiveUsageSessions.find(deviceId);
323         LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
324         auto& [_, activeSession] = *activeSessionIt;
325         mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
326         mActiveUsageSessions.erase(activeSessionIt);
327     }
328 }
329 
330 // --- InputDeviceMetricsCollector::ActiveSession ---
331 
ActiveSession(nanoseconds usageSessionTimeout,nanoseconds startTime)332 InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
333                                                           nanoseconds startTime)
334       : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
335 
recordUsage(nanoseconds eventTime,InputDeviceUsageSource source)336 void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
337                                                              InputDeviceUsageSource source) {
338     // We assume that event times for subsequent events are always monotonically increasing for each
339     // input device.
340     auto [activeSourceIt, inserted] =
341             mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
342     if (!inserted) {
343         activeSourceIt->second.end = eventTime;
344     }
345     mDeviceSession.end = eventTime;
346 }
347 
recordInteraction(const Interaction & interaction)348 void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
349     const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
350     const auto timestamp = std::get<nanoseconds>(interaction);
351     if (timestamp >= sessionExpiryTime) {
352         // This interaction occurred after the device's current active session is set to expire.
353         // Ignore it.
354         return;
355     }
356 
357     for (Uid uid : std::get<std::set<Uid>>(interaction)) {
358         auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
359         if (!inserted) {
360             activeUidIt->second.end = timestamp;
361         }
362     }
363 }
364 
checkIfCompletedAt(nanoseconds timestamp)365 bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
366     const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
367     std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
368     for (auto& [source, session] : mActiveSessionsBySource) {
369         if (session.end <= sessionExpiryTime) {
370             completedSourceSessionsForDevice.emplace_back(source);
371         }
372     }
373     for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
374         auto it = mActiveSessionsBySource.find(source);
375         const auto& [_, session] = *it;
376         mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
377         mActiveSessionsBySource.erase(it);
378     }
379 
380     std::vector<Uid> completedUidSessionsForDevice;
381     for (auto& [uid, session] : mActiveSessionsByUid) {
382         if (session.end <= sessionExpiryTime) {
383             completedUidSessionsForDevice.emplace_back(uid);
384         }
385     }
386     for (Uid uid : completedUidSessionsForDevice) {
387         auto it = mActiveSessionsByUid.find(uid);
388         const auto& [_, session] = *it;
389         mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
390         mActiveSessionsByUid.erase(it);
391     }
392 
393     // This active session has expired if there are no more active source sessions tracked.
394     return mActiveSessionsBySource.empty();
395 }
396 
397 InputDeviceMetricsLogger::DeviceUsageReport
finishSession()398 InputDeviceMetricsCollector::ActiveSession::finishSession() {
399     const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
400 
401     for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
402         mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
403     }
404     mActiveSessionsBySource.clear();
405 
406     for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
407         mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
408     }
409     mActiveSessionsByUid.clear();
410 
411     return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
412 }
413 
414 } // namespace android
415