1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2023 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "InputDeviceMetricsCollector"
18*38e8c45fSAndroid Build Coastguard Worker #include "InputDeviceMetricsCollector.h"
19*38e8c45fSAndroid Build Coastguard Worker
20*38e8c45fSAndroid Build Coastguard Worker #include "InputDeviceMetricsSource.h"
21*38e8c45fSAndroid Build Coastguard Worker
22*38e8c45fSAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
23*38e8c45fSAndroid Build Coastguard Worker #include <input/PrintTools.h>
24*38e8c45fSAndroid Build Coastguard Worker
25*38e8c45fSAndroid Build Coastguard Worker namespace android {
26*38e8c45fSAndroid Build Coastguard Worker
27*38e8c45fSAndroid Build Coastguard Worker using android::base::StringPrintf;
28*38e8c45fSAndroid Build Coastguard Worker using std::chrono::nanoseconds;
29*38e8c45fSAndroid Build Coastguard Worker using std::chrono_literals::operator""ns;
30*38e8c45fSAndroid Build Coastguard Worker
31*38e8c45fSAndroid Build Coastguard Worker namespace {
32*38e8c45fSAndroid Build Coastguard Worker
33*38e8c45fSAndroid Build Coastguard Worker constexpr nanoseconds DEFAULT_USAGE_SESSION_TIMEOUT = std::chrono::minutes(2);
34*38e8c45fSAndroid Build Coastguard Worker
35*38e8c45fSAndroid Build Coastguard Worker /**
36*38e8c45fSAndroid Build Coastguard Worker * Log debug messages about metrics events logged to statsd.
37*38e8c45fSAndroid Build Coastguard Worker * Enable this via "adb shell setprop log.tag.InputDeviceMetricsCollector DEBUG" (requires restart)
38*38e8c45fSAndroid Build Coastguard Worker */
39*38e8c45fSAndroid Build Coastguard Worker const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
40*38e8c45fSAndroid Build Coastguard Worker
41*38e8c45fSAndroid Build Coastguard Worker constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
42*38e8c45fSAndroid Build Coastguard Worker
linuxBusToInputDeviceBusEnum(int32_t linuxBus,bool isUsiStylus)43*38e8c45fSAndroid Build Coastguard Worker int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
44*38e8c45fSAndroid Build Coastguard Worker if (isUsiStylus) {
45*38e8c45fSAndroid Build Coastguard Worker // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
46*38e8c45fSAndroid Build Coastguard Worker // For metrics purposes, we treat this protocol as a separate bus.
47*38e8c45fSAndroid Build Coastguard Worker return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
48*38e8c45fSAndroid Build Coastguard Worker }
49*38e8c45fSAndroid Build Coastguard Worker
50*38e8c45fSAndroid Build Coastguard Worker // When adding cases to this switch, also add them to the copy of this method in
51*38e8c45fSAndroid Build Coastguard Worker // TouchpadInputMapper.cpp.
52*38e8c45fSAndroid Build Coastguard Worker // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
53*38e8c45fSAndroid Build Coastguard Worker switch (linuxBus) {
54*38e8c45fSAndroid Build Coastguard Worker case BUS_USB:
55*38e8c45fSAndroid Build Coastguard Worker return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
56*38e8c45fSAndroid Build Coastguard Worker case BUS_BLUETOOTH:
57*38e8c45fSAndroid Build Coastguard Worker return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
58*38e8c45fSAndroid Build Coastguard Worker default:
59*38e8c45fSAndroid Build Coastguard Worker return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
60*38e8c45fSAndroid Build Coastguard Worker }
61*38e8c45fSAndroid Build Coastguard Worker }
62*38e8c45fSAndroid Build Coastguard Worker
63*38e8c45fSAndroid Build Coastguard Worker class : public InputDeviceMetricsLogger {
getCurrentTime()64*38e8c45fSAndroid Build Coastguard Worker nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
65*38e8c45fSAndroid Build Coastguard Worker
logInputDeviceUsageReported(const MetricsDeviceInfo & info,const DeviceUsageReport & report)66*38e8c45fSAndroid Build Coastguard Worker void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
67*38e8c45fSAndroid Build Coastguard Worker const DeviceUsageReport& report) override {
68*38e8c45fSAndroid Build Coastguard Worker const int32_t durationMillis =
69*38e8c45fSAndroid Build Coastguard Worker std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
70*38e8c45fSAndroid Build Coastguard Worker const static std::vector<int32_t> empty;
71*38e8c45fSAndroid Build Coastguard Worker
72*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
73*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
74*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, " Source breakdown:");
75*38e8c45fSAndroid Build Coastguard Worker
76*38e8c45fSAndroid Build Coastguard Worker std::vector<int32_t> sources;
77*38e8c45fSAndroid Build Coastguard Worker std::vector<int32_t> durationsPerSource;
78*38e8c45fSAndroid Build Coastguard Worker for (auto& [src, dur] : report.sourceBreakdown) {
79*38e8c45fSAndroid Build Coastguard Worker sources.push_back(ftl::to_underlying(src));
80*38e8c45fSAndroid Build Coastguard Worker int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
81*38e8c45fSAndroid Build Coastguard Worker durationsPerSource.emplace_back(durMillis);
82*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, " - usageSource: %s\t duration: %dms",
83*38e8c45fSAndroid Build Coastguard Worker ftl::enum_string(src).c_str(), durMillis);
84*38e8c45fSAndroid Build Coastguard Worker }
85*38e8c45fSAndroid Build Coastguard Worker
86*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, " Uid breakdown:");
87*38e8c45fSAndroid Build Coastguard Worker
88*38e8c45fSAndroid Build Coastguard Worker std::vector<int32_t> uids;
89*38e8c45fSAndroid Build Coastguard Worker std::vector<int32_t> durationsPerUid;
90*38e8c45fSAndroid Build Coastguard Worker for (auto& [uid, dur] : report.uidBreakdown) {
91*38e8c45fSAndroid Build Coastguard Worker uids.push_back(uid.val());
92*38e8c45fSAndroid Build Coastguard Worker int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
93*38e8c45fSAndroid Build Coastguard Worker durationsPerUid.push_back(durMillis);
94*38e8c45fSAndroid Build Coastguard Worker ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
95*38e8c45fSAndroid Build Coastguard Worker durMillis);
96*38e8c45fSAndroid Build Coastguard Worker }
97*38e8c45fSAndroid Build Coastguard Worker util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
98*38e8c45fSAndroid Build Coastguard Worker linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
99*38e8c45fSAndroid Build Coastguard Worker sources, durationsPerSource, uids, durationsPerUid);
100*38e8c45fSAndroid Build Coastguard Worker }
101*38e8c45fSAndroid Build Coastguard Worker } sStatsdLogger;
102*38e8c45fSAndroid Build Coastguard Worker
isIgnoredInputDeviceId(int32_t deviceId)103*38e8c45fSAndroid Build Coastguard Worker bool isIgnoredInputDeviceId(int32_t deviceId) {
104*38e8c45fSAndroid Build Coastguard Worker switch (deviceId) {
105*38e8c45fSAndroid Build Coastguard Worker case INVALID_INPUT_DEVICE_ID:
106*38e8c45fSAndroid Build Coastguard Worker case VIRTUAL_KEYBOARD_ID:
107*38e8c45fSAndroid Build Coastguard Worker return true;
108*38e8c45fSAndroid Build Coastguard Worker default:
109*38e8c45fSAndroid Build Coastguard Worker return false;
110*38e8c45fSAndroid Build Coastguard Worker }
111*38e8c45fSAndroid Build Coastguard Worker }
112*38e8c45fSAndroid Build Coastguard Worker
113*38e8c45fSAndroid Build Coastguard Worker } // namespace
114*38e8c45fSAndroid Build Coastguard Worker
InputDeviceMetricsCollector(InputListenerInterface & listener)115*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
116*38e8c45fSAndroid Build Coastguard Worker : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
117*38e8c45fSAndroid Build Coastguard Worker
InputDeviceMetricsCollector(InputListenerInterface & listener,InputDeviceMetricsLogger & logger,nanoseconds usageSessionTimeout)118*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener,
119*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsLogger& logger,
120*38e8c45fSAndroid Build Coastguard Worker nanoseconds usageSessionTimeout)
121*38e8c45fSAndroid Build Coastguard Worker : mNextListener(listener),
122*38e8c45fSAndroid Build Coastguard Worker mLogger(logger),
123*38e8c45fSAndroid Build Coastguard Worker mUsageSessionTimeout(usageSessionTimeout),
124*38e8c45fSAndroid Build Coastguard Worker mInteractionsQueue(INTERACTIONS_QUEUE_CAPACITY) {}
125*38e8c45fSAndroid Build Coastguard Worker
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)126*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyInputDevicesChanged(
127*38e8c45fSAndroid Build Coastguard Worker const NotifyInputDevicesChangedArgs& args) {
128*38e8c45fSAndroid Build Coastguard Worker {
129*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
130*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
131*38e8c45fSAndroid Build Coastguard Worker onInputDevicesChanged(args.inputDeviceInfos);
132*38e8c45fSAndroid Build Coastguard Worker }
133*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
134*38e8c45fSAndroid Build Coastguard Worker }
135*38e8c45fSAndroid Build Coastguard Worker
notifyKey(const NotifyKeyArgs & args)136*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
137*38e8c45fSAndroid Build Coastguard Worker {
138*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
139*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
140*38e8c45fSAndroid Build Coastguard Worker const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
141*38e8c45fSAndroid Build Coastguard Worker return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
142*38e8c45fSAndroid Build Coastguard Worker };
143*38e8c45fSAndroid Build Coastguard Worker onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
144*38e8c45fSAndroid Build Coastguard Worker }
145*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
146*38e8c45fSAndroid Build Coastguard Worker }
147*38e8c45fSAndroid Build Coastguard Worker
notifyMotion(const NotifyMotionArgs & args)148*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
149*38e8c45fSAndroid Build Coastguard Worker {
150*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
151*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
152*38e8c45fSAndroid Build Coastguard Worker onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
153*38e8c45fSAndroid Build Coastguard Worker [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
154*38e8c45fSAndroid Build Coastguard Worker }
155*38e8c45fSAndroid Build Coastguard Worker
156*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
157*38e8c45fSAndroid Build Coastguard Worker }
158*38e8c45fSAndroid Build Coastguard Worker
notifySwitch(const NotifySwitchArgs & args)159*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
160*38e8c45fSAndroid Build Coastguard Worker {
161*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
162*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
163*38e8c45fSAndroid Build Coastguard Worker }
164*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
165*38e8c45fSAndroid Build Coastguard Worker }
166*38e8c45fSAndroid Build Coastguard Worker
notifySensor(const NotifySensorArgs & args)167*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
168*38e8c45fSAndroid Build Coastguard Worker {
169*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
170*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
171*38e8c45fSAndroid Build Coastguard Worker }
172*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
173*38e8c45fSAndroid Build Coastguard Worker }
174*38e8c45fSAndroid Build Coastguard Worker
notifyVibratorState(const NotifyVibratorStateArgs & args)175*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
176*38e8c45fSAndroid Build Coastguard Worker {
177*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
178*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
179*38e8c45fSAndroid Build Coastguard Worker }
180*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
181*38e8c45fSAndroid Build Coastguard Worker }
182*38e8c45fSAndroid Build Coastguard Worker
notifyDeviceReset(const NotifyDeviceResetArgs & args)183*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
184*38e8c45fSAndroid Build Coastguard Worker {
185*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
186*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
187*38e8c45fSAndroid Build Coastguard Worker }
188*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
189*38e8c45fSAndroid Build Coastguard Worker }
190*38e8c45fSAndroid Build Coastguard Worker
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)191*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
192*38e8c45fSAndroid Build Coastguard Worker const NotifyPointerCaptureChangedArgs& args) {
193*38e8c45fSAndroid Build Coastguard Worker {
194*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
195*38e8c45fSAndroid Build Coastguard Worker reportCompletedSessions();
196*38e8c45fSAndroid Build Coastguard Worker }
197*38e8c45fSAndroid Build Coastguard Worker mNextListener.notify(args);
198*38e8c45fSAndroid Build Coastguard Worker }
199*38e8c45fSAndroid Build Coastguard Worker
notifyDeviceInteraction(int32_t deviceId,nsecs_t timestamp,const std::set<Uid> & uids)200*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
201*38e8c45fSAndroid Build Coastguard Worker const std::set<Uid>& uids) {
202*38e8c45fSAndroid Build Coastguard Worker if (isIgnoredInputDeviceId(deviceId)) {
203*38e8c45fSAndroid Build Coastguard Worker return;
204*38e8c45fSAndroid Build Coastguard Worker }
205*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
206*38e8c45fSAndroid Build Coastguard Worker mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
207*38e8c45fSAndroid Build Coastguard Worker }
208*38e8c45fSAndroid Build Coastguard Worker
dump(std::string & dump)209*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::dump(std::string& dump) {
210*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
211*38e8c45fSAndroid Build Coastguard Worker dump += "InputDeviceMetricsCollector:\n";
212*38e8c45fSAndroid Build Coastguard Worker
213*38e8c45fSAndroid Build Coastguard Worker dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
214*38e8c45fSAndroid Build Coastguard Worker dump += " Devices with active usage sessions: " +
215*38e8c45fSAndroid Build Coastguard Worker dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
216*38e8c45fSAndroid Build Coastguard Worker }
217*38e8c45fSAndroid Build Coastguard Worker
monitor()218*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::monitor() {
219*38e8c45fSAndroid Build Coastguard Worker std::scoped_lock lock(mLock);
220*38e8c45fSAndroid Build Coastguard Worker }
221*38e8c45fSAndroid Build Coastguard Worker
onInputDevicesChanged(const std::vector<InputDeviceInfo> & infos)222*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
223*38e8c45fSAndroid Build Coastguard Worker std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
224*38e8c45fSAndroid Build Coastguard Worker
225*38e8c45fSAndroid Build Coastguard Worker for (const InputDeviceInfo& info : infos) {
226*38e8c45fSAndroid Build Coastguard Worker if (isIgnoredInputDeviceId(info.getId())) {
227*38e8c45fSAndroid Build Coastguard Worker continue;
228*38e8c45fSAndroid Build Coastguard Worker }
229*38e8c45fSAndroid Build Coastguard Worker const auto& i = info.getIdentifier();
230*38e8c45fSAndroid Build Coastguard Worker newDeviceInfos.emplace(info.getId(),
231*38e8c45fSAndroid Build Coastguard Worker MetricsDeviceInfo{
232*38e8c45fSAndroid Build Coastguard Worker .deviceId = info.getId(),
233*38e8c45fSAndroid Build Coastguard Worker .vendor = i.vendor,
234*38e8c45fSAndroid Build Coastguard Worker .product = i.product,
235*38e8c45fSAndroid Build Coastguard Worker .version = i.version,
236*38e8c45fSAndroid Build Coastguard Worker .bus = i.bus,
237*38e8c45fSAndroid Build Coastguard Worker .isUsiStylus = info.getUsiVersion().has_value(),
238*38e8c45fSAndroid Build Coastguard Worker .keyboardType = info.getKeyboardType(),
239*38e8c45fSAndroid Build Coastguard Worker });
240*38e8c45fSAndroid Build Coastguard Worker }
241*38e8c45fSAndroid Build Coastguard Worker
242*38e8c45fSAndroid Build Coastguard Worker for (auto [deviceId, info] : mLoggedDeviceInfos) {
243*38e8c45fSAndroid Build Coastguard Worker if (newDeviceInfos.count(deviceId) != 0) {
244*38e8c45fSAndroid Build Coastguard Worker continue;
245*38e8c45fSAndroid Build Coastguard Worker }
246*38e8c45fSAndroid Build Coastguard Worker onInputDeviceRemoved(deviceId, info);
247*38e8c45fSAndroid Build Coastguard Worker }
248*38e8c45fSAndroid Build Coastguard Worker
249*38e8c45fSAndroid Build Coastguard Worker std::swap(newDeviceInfos, mLoggedDeviceInfos);
250*38e8c45fSAndroid Build Coastguard Worker }
251*38e8c45fSAndroid Build Coastguard Worker
onInputDeviceRemoved(DeviceId deviceId,const MetricsDeviceInfo & info)252*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
253*38e8c45fSAndroid Build Coastguard Worker const MetricsDeviceInfo& info) {
254*38e8c45fSAndroid Build Coastguard Worker auto it = mActiveUsageSessions.find(deviceId);
255*38e8c45fSAndroid Build Coastguard Worker if (it == mActiveUsageSessions.end()) {
256*38e8c45fSAndroid Build Coastguard Worker return;
257*38e8c45fSAndroid Build Coastguard Worker }
258*38e8c45fSAndroid Build Coastguard Worker // Report usage for that device if there is an active session.
259*38e8c45fSAndroid Build Coastguard Worker auto& [_, activeSession] = *it;
260*38e8c45fSAndroid Build Coastguard Worker mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
261*38e8c45fSAndroid Build Coastguard Worker mActiveUsageSessions.erase(it);
262*38e8c45fSAndroid Build Coastguard Worker
263*38e8c45fSAndroid Build Coastguard Worker // We don't remove this from mLoggedDeviceInfos because it will be updated in
264*38e8c45fSAndroid Build Coastguard Worker // onInputDevicesChanged().
265*38e8c45fSAndroid Build Coastguard Worker }
266*38e8c45fSAndroid Build Coastguard Worker
onInputDeviceUsage(DeviceId deviceId,nanoseconds eventTime,const SourceProvider & getSources)267*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseconds eventTime,
268*38e8c45fSAndroid Build Coastguard Worker const SourceProvider& getSources) {
269*38e8c45fSAndroid Build Coastguard Worker auto infoIt = mLoggedDeviceInfos.find(deviceId);
270*38e8c45fSAndroid Build Coastguard Worker if (infoIt == mLoggedDeviceInfos.end()) {
271*38e8c45fSAndroid Build Coastguard Worker // Do not track usage for devices that are not logged.
272*38e8c45fSAndroid Build Coastguard Worker return;
273*38e8c45fSAndroid Build Coastguard Worker }
274*38e8c45fSAndroid Build Coastguard Worker
275*38e8c45fSAndroid Build Coastguard Worker auto [sessionIt, _] =
276*38e8c45fSAndroid Build Coastguard Worker mActiveUsageSessions.try_emplace(deviceId, mUsageSessionTimeout, eventTime);
277*38e8c45fSAndroid Build Coastguard Worker for (InputDeviceUsageSource source : getSources(infoIt->second)) {
278*38e8c45fSAndroid Build Coastguard Worker sessionIt->second.recordUsage(eventTime, source);
279*38e8c45fSAndroid Build Coastguard Worker }
280*38e8c45fSAndroid Build Coastguard Worker }
281*38e8c45fSAndroid Build Coastguard Worker
onInputDeviceInteraction(const Interaction & interaction)282*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
283*38e8c45fSAndroid Build Coastguard Worker auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
284*38e8c45fSAndroid Build Coastguard Worker if (activeSessionIt == mActiveUsageSessions.end()) {
285*38e8c45fSAndroid Build Coastguard Worker return;
286*38e8c45fSAndroid Build Coastguard Worker }
287*38e8c45fSAndroid Build Coastguard Worker
288*38e8c45fSAndroid Build Coastguard Worker activeSessionIt->second.recordInteraction(interaction);
289*38e8c45fSAndroid Build Coastguard Worker }
290*38e8c45fSAndroid Build Coastguard Worker
reportCompletedSessions()291*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::reportCompletedSessions() {
292*38e8c45fSAndroid Build Coastguard Worker // Process all pending interactions.
293*38e8c45fSAndroid Build Coastguard Worker for (auto interaction = mInteractionsQueue.pop(); interaction;
294*38e8c45fSAndroid Build Coastguard Worker interaction = mInteractionsQueue.pop()) {
295*38e8c45fSAndroid Build Coastguard Worker onInputDeviceInteraction(*interaction);
296*38e8c45fSAndroid Build Coastguard Worker }
297*38e8c45fSAndroid Build Coastguard Worker
298*38e8c45fSAndroid Build Coastguard Worker const auto currentTime = mLogger.getCurrentTime();
299*38e8c45fSAndroid Build Coastguard Worker std::vector<DeviceId> completedUsageSessions;
300*38e8c45fSAndroid Build Coastguard Worker
301*38e8c45fSAndroid Build Coastguard Worker // Process usages for all active session to determine if any sessions have expired.
302*38e8c45fSAndroid Build Coastguard Worker for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
303*38e8c45fSAndroid Build Coastguard Worker if (activeSession.checkIfCompletedAt(currentTime)) {
304*38e8c45fSAndroid Build Coastguard Worker completedUsageSessions.emplace_back(deviceId);
305*38e8c45fSAndroid Build Coastguard Worker }
306*38e8c45fSAndroid Build Coastguard Worker }
307*38e8c45fSAndroid Build Coastguard Worker
308*38e8c45fSAndroid Build Coastguard Worker // Close out and log all expired usage sessions.
309*38e8c45fSAndroid Build Coastguard Worker for (DeviceId deviceId : completedUsageSessions) {
310*38e8c45fSAndroid Build Coastguard Worker const auto infoIt = mLoggedDeviceInfos.find(deviceId);
311*38e8c45fSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
312*38e8c45fSAndroid Build Coastguard Worker
313*38e8c45fSAndroid Build Coastguard Worker auto activeSessionIt = mActiveUsageSessions.find(deviceId);
314*38e8c45fSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
315*38e8c45fSAndroid Build Coastguard Worker auto& [_, activeSession] = *activeSessionIt;
316*38e8c45fSAndroid Build Coastguard Worker mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
317*38e8c45fSAndroid Build Coastguard Worker mActiveUsageSessions.erase(activeSessionIt);
318*38e8c45fSAndroid Build Coastguard Worker }
319*38e8c45fSAndroid Build Coastguard Worker }
320*38e8c45fSAndroid Build Coastguard Worker
321*38e8c45fSAndroid Build Coastguard Worker // --- InputDeviceMetricsCollector::ActiveSession ---
322*38e8c45fSAndroid Build Coastguard Worker
ActiveSession(nanoseconds usageSessionTimeout,nanoseconds startTime)323*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::ActiveSession::ActiveSession(nanoseconds usageSessionTimeout,
324*38e8c45fSAndroid Build Coastguard Worker nanoseconds startTime)
325*38e8c45fSAndroid Build Coastguard Worker : mUsageSessionTimeout(usageSessionTimeout), mDeviceSession({startTime, startTime}) {}
326*38e8c45fSAndroid Build Coastguard Worker
recordUsage(nanoseconds eventTime,InputDeviceUsageSource source)327*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTime,
328*38e8c45fSAndroid Build Coastguard Worker InputDeviceUsageSource source) {
329*38e8c45fSAndroid Build Coastguard Worker // We assume that event times for subsequent events are always monotonically increasing for each
330*38e8c45fSAndroid Build Coastguard Worker // input device.
331*38e8c45fSAndroid Build Coastguard Worker auto [activeSourceIt, inserted] =
332*38e8c45fSAndroid Build Coastguard Worker mActiveSessionsBySource.try_emplace(source, eventTime, eventTime);
333*38e8c45fSAndroid Build Coastguard Worker if (!inserted) {
334*38e8c45fSAndroid Build Coastguard Worker activeSourceIt->second.end = eventTime;
335*38e8c45fSAndroid Build Coastguard Worker }
336*38e8c45fSAndroid Build Coastguard Worker mDeviceSession.end = eventTime;
337*38e8c45fSAndroid Build Coastguard Worker }
338*38e8c45fSAndroid Build Coastguard Worker
recordInteraction(const Interaction & interaction)339*38e8c45fSAndroid Build Coastguard Worker void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
340*38e8c45fSAndroid Build Coastguard Worker const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
341*38e8c45fSAndroid Build Coastguard Worker const auto timestamp = std::get<nanoseconds>(interaction);
342*38e8c45fSAndroid Build Coastguard Worker if (timestamp >= sessionExpiryTime) {
343*38e8c45fSAndroid Build Coastguard Worker // This interaction occurred after the device's current active session is set to expire.
344*38e8c45fSAndroid Build Coastguard Worker // Ignore it.
345*38e8c45fSAndroid Build Coastguard Worker return;
346*38e8c45fSAndroid Build Coastguard Worker }
347*38e8c45fSAndroid Build Coastguard Worker
348*38e8c45fSAndroid Build Coastguard Worker for (Uid uid : std::get<std::set<Uid>>(interaction)) {
349*38e8c45fSAndroid Build Coastguard Worker auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
350*38e8c45fSAndroid Build Coastguard Worker if (!inserted) {
351*38e8c45fSAndroid Build Coastguard Worker activeUidIt->second.end = timestamp;
352*38e8c45fSAndroid Build Coastguard Worker }
353*38e8c45fSAndroid Build Coastguard Worker }
354*38e8c45fSAndroid Build Coastguard Worker }
355*38e8c45fSAndroid Build Coastguard Worker
checkIfCompletedAt(nanoseconds timestamp)356*38e8c45fSAndroid Build Coastguard Worker bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
357*38e8c45fSAndroid Build Coastguard Worker const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
358*38e8c45fSAndroid Build Coastguard Worker std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
359*38e8c45fSAndroid Build Coastguard Worker for (auto& [source, session] : mActiveSessionsBySource) {
360*38e8c45fSAndroid Build Coastguard Worker if (session.end <= sessionExpiryTime) {
361*38e8c45fSAndroid Build Coastguard Worker completedSourceSessionsForDevice.emplace_back(source);
362*38e8c45fSAndroid Build Coastguard Worker }
363*38e8c45fSAndroid Build Coastguard Worker }
364*38e8c45fSAndroid Build Coastguard Worker for (InputDeviceUsageSource source : completedSourceSessionsForDevice) {
365*38e8c45fSAndroid Build Coastguard Worker auto it = mActiveSessionsBySource.find(source);
366*38e8c45fSAndroid Build Coastguard Worker const auto& [_, session] = *it;
367*38e8c45fSAndroid Build Coastguard Worker mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
368*38e8c45fSAndroid Build Coastguard Worker mActiveSessionsBySource.erase(it);
369*38e8c45fSAndroid Build Coastguard Worker }
370*38e8c45fSAndroid Build Coastguard Worker
371*38e8c45fSAndroid Build Coastguard Worker std::vector<Uid> completedUidSessionsForDevice;
372*38e8c45fSAndroid Build Coastguard Worker for (auto& [uid, session] : mActiveSessionsByUid) {
373*38e8c45fSAndroid Build Coastguard Worker if (session.end <= sessionExpiryTime) {
374*38e8c45fSAndroid Build Coastguard Worker completedUidSessionsForDevice.emplace_back(uid);
375*38e8c45fSAndroid Build Coastguard Worker }
376*38e8c45fSAndroid Build Coastguard Worker }
377*38e8c45fSAndroid Build Coastguard Worker for (Uid uid : completedUidSessionsForDevice) {
378*38e8c45fSAndroid Build Coastguard Worker auto it = mActiveSessionsByUid.find(uid);
379*38e8c45fSAndroid Build Coastguard Worker const auto& [_, session] = *it;
380*38e8c45fSAndroid Build Coastguard Worker mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
381*38e8c45fSAndroid Build Coastguard Worker mActiveSessionsByUid.erase(it);
382*38e8c45fSAndroid Build Coastguard Worker }
383*38e8c45fSAndroid Build Coastguard Worker
384*38e8c45fSAndroid Build Coastguard Worker // This active session has expired if there are no more active source sessions tracked.
385*38e8c45fSAndroid Build Coastguard Worker return mActiveSessionsBySource.empty();
386*38e8c45fSAndroid Build Coastguard Worker }
387*38e8c45fSAndroid Build Coastguard Worker
388*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsLogger::DeviceUsageReport
finishSession()389*38e8c45fSAndroid Build Coastguard Worker InputDeviceMetricsCollector::ActiveSession::finishSession() {
390*38e8c45fSAndroid Build Coastguard Worker const auto deviceUsageDuration = mDeviceSession.end - mDeviceSession.start;
391*38e8c45fSAndroid Build Coastguard Worker
392*38e8c45fSAndroid Build Coastguard Worker for (const auto& [source, sourceSession] : mActiveSessionsBySource) {
393*38e8c45fSAndroid Build Coastguard Worker mSourceUsageBreakdown.emplace_back(source, sourceSession.end - sourceSession.start);
394*38e8c45fSAndroid Build Coastguard Worker }
395*38e8c45fSAndroid Build Coastguard Worker mActiveSessionsBySource.clear();
396*38e8c45fSAndroid Build Coastguard Worker
397*38e8c45fSAndroid Build Coastguard Worker for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
398*38e8c45fSAndroid Build Coastguard Worker mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
399*38e8c45fSAndroid Build Coastguard Worker }
400*38e8c45fSAndroid Build Coastguard Worker mActiveSessionsByUid.clear();
401*38e8c45fSAndroid Build Coastguard Worker
402*38e8c45fSAndroid Build Coastguard Worker return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
403*38e8c45fSAndroid Build Coastguard Worker }
404*38e8c45fSAndroid Build Coastguard Worker
405*38e8c45fSAndroid Build Coastguard Worker } // namespace android
406