1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "InputDeviceMetricsSource.h"
#include "InputListener.h"
#include "NotifyArgs.h"
#include "SyncQueue.h"
#include <android-base/thread_annotations.h>
#include <ftl/mixins.h>
#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
#include <chrono>
#include <functional>
#include <map>
#include <mutex>
#include <set>
#include <vector>
namespace android {
/**
* Logs metrics about registered input devices and their usages.
*/
class InputDeviceMetricsCollectorInterface : public InputListenerInterface {
public:
/**
* Notify the metrics collector that there was an input device interaction with apps.
* Called from the InputDispatcher thread.
*/
virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
/**
* Dump the state of the interaction blocker.
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
/** Called by the heartbeat to ensure that this component has not deadlocked. */
virtual void monitor() = 0;
};
/** The logging interface for the metrics collector, injected for testing. */
class InputDeviceMetricsLogger {
public:
virtual std::chrono::nanoseconds getCurrentTime() = 0;
// Describes the breakdown of an input device usage session by its usage sources.
// An input device can have more than one usage source. For example, some game controllers have
// buttons, joysticks, and touchpads. We track usage by these sources to get a better picture of
// the device usage. The source breakdown of a 10 minute usage session could look like this:
// { {GAMEPAD, <9 mins>}, {TOUCHPAD, <2 mins>}, {TOUCHPAD, <3 mins>} }
// This would indicate that the GAMEPAD source was used first, and that source usage session
// lasted for 9 mins. During that time, the TOUCHPAD was used for 2 mins, until its source
// usage session expired. The TOUCHPAD was then used again later for another 3 mins.
using SourceUsageBreakdown =
std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>;
// Describes the breakdown of an input device usage session by the UIDs that it interacted with.
using UidUsageBreakdown =
std::vector<std::pair<gui::Uid, std::chrono::nanoseconds /*duration*/>>;
struct DeviceUsageReport {
std::chrono::nanoseconds usageDuration;
SourceUsageBreakdown sourceBreakdown;
UidUsageBreakdown uidBreakdown;
};
// A subset of information from the InputDeviceInfo class that is used for metrics collection,
// used to avoid copying and storing all of the fields and strings in InputDeviceInfo.
struct MetricsDeviceInfo {
int32_t deviceId;
int32_t vendor;
int32_t product;
int32_t version;
int32_t bus;
bool isUsiStylus;
int32_t keyboardType;
};
virtual void logInputDeviceUsageReported(const MetricsDeviceInfo&,
const DeviceUsageReport&) = 0;
virtual ~InputDeviceMetricsLogger() = default;
};
class InputDeviceMetricsCollector : public InputDeviceMetricsCollectorInterface {
public:
explicit InputDeviceMetricsCollector(InputListenerInterface& listener);
~InputDeviceMetricsCollector() override = default;
// Test constructor
InputDeviceMetricsCollector(InputListenerInterface& listener, InputDeviceMetricsLogger& logger,
std::chrono::nanoseconds usageSessionTimeout);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
void notifySensor(const NotifySensorArgs& args) override;
void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override;
void dump(std::string& dump) override;
void monitor() override;
private:
std::mutex mLock;
InputListenerInterface& mNextListener;
InputDeviceMetricsLogger& mLogger GUARDED_BY(mLock);
const std::chrono::nanoseconds mUsageSessionTimeout;
// Type-safe wrapper for input device id.
struct DeviceId : ftl::Constructible<DeviceId, std::int32_t>,
ftl::Equatable<DeviceId>,
ftl::Orderable<DeviceId> {
using Constructible::Constructible;
};
static inline std::string toString(const DeviceId& id) {
return std::to_string(ftl::to_underlying(id));
}
using Uid = gui::Uid;
using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo;
std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos GUARDED_BY(mLock);
using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
SyncQueue<Interaction> mInteractionsQueue GUARDED_BY(mLock);
class ActiveSession {
public:
explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout,
std::chrono::nanoseconds startTime);
void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source);
void recordInteraction(const Interaction&);
bool checkIfCompletedAt(std::chrono::nanoseconds timestamp);
InputDeviceMetricsLogger::DeviceUsageReport finishSession();
private:
struct UsageSession {
std::chrono::nanoseconds start{};
std::chrono::nanoseconds end{};
};
const std::chrono::nanoseconds mUsageSessionTimeout;
UsageSession mDeviceSession{};
std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{};
InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{};
std::map<Uid, UsageSession> mActiveSessionsByUid{};
InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{};
};
// The input devices that currently have active usage sessions.
std::map<DeviceId, ActiveSession> mActiveUsageSessions GUARDED_BY(mLock);
void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) REQUIRES(mLock);
void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info) REQUIRES(mLock);
using SourceProvider =
std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
const SourceProvider& getSources) REQUIRES(mLock);
void onInputDeviceInteraction(const Interaction&) REQUIRES(mLock);
void reportCompletedSessions() REQUIRES(mLock);
};
} // namespace android
|