1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "event_recorder.h"
17
18 #include <algorithm>
19 #include <iostream>
20 #include <unistd.h>
21 #include <string>
22
23 #include <sys/select.h>
24
25 #include "event_utils.h"
26
27 namespace OHOS {
28 namespace MMI {
29 namespace {
30 constexpr suseconds_t TIME_OUT = 100000;
31 }
EventRecorder(const std::string & outputPath)32 EventRecorder::EventRecorder(const std::string& outputPath)
33 : outputPath_(outputPath), running_(false)
34 {
35 }
36
~EventRecorder()37 EventRecorder::~EventRecorder()
38 {
39 Stop();
40 }
41
Start(std::vector<InputDevice> & devices)42 bool EventRecorder::Start(std::vector<InputDevice>& devices)
43 {
44 if (running_) {
45 return false;
46 }
47 if (devices.empty()) {
48 PrintError("No devices to record from");
49 return false;
50 }
51 // ensure the out coming path safe
52 char resolvedPath[PATH_MAX] = {};
53 if (realpath(outputPath_.c_str(), resolvedPath) == nullptr) {
54 PrintError("Realpath failed. path:%{private}s", outputPath_.c_str());
55 return false;
56 }
57 outputFile_.open(resolvedPath, std::ios::binary | std::ios::trunc);
58 if (!outputFile_) {
59 PrintError("Failed to open output file: %s", resolvedPath);
60 return false;
61 }
62 devices_.clear();
63 deviceEventBuffers_.clear();
64 for (InputDevice& device : devices) {
65 PrintInfo("Recording from device %u: %s (%s)",
66 device.GetId(), device.GetPath().c_str(), device.GetName().c_str());
67 if (!device.IsOpen() && !device.OpenForReading()) {
68 PrintError("Failed to open device for reading: %s", device.GetPath().c_str());
69 continue;
70 }
71 devices_.push_back(std::move(device));
72 }
73 bool hasValidDevices = false;
74 for (const auto& device : devices_) {
75 if (device.IsOpen()) {
76 hasValidDevices = true;
77 break;
78 }
79 }
80 if (!hasValidDevices) {
81 PrintError("No devices could be opened for recording");
82 outputFile_.close();
83 return false;
84 }
85 running_ = true;
86 outputFile_ << "EVENTS_BEGIN" << std::endl;
87 MainLoop();
88 return true;
89 }
90
Stop()91 void EventRecorder::Stop()
92 {
93 if (!running_) {
94 return;
95 }
96 running_ = false;
97 if (outputFile_.is_open()) {
98 outputFile_ << "EVENTS_END" << std::endl<<std::endl;
99 outputFile_ << "DEVICES: " << deviceEventBuffers_.size() << std::endl;
100 std::cout << "DEVICES: " << deviceEventBuffers_.size() << std::endl;
101 for (const auto& device : devices_) {
102 if (deviceEventBuffers_.find(device.GetId()) != deviceEventBuffers_.end()) {
103 std::string deviceName = device.GetName();
104 std::replace(deviceName.begin(), deviceName.end(), '|', '_');
105 outputFile_ << "DEVICE: " << device.GetId() << "|" << device.GetPath() << "|" << deviceName << "|";
106 outputFile_ << device.GetHash() << std::endl;
107 std::cout << "DEVICE: " << device.GetId() << "|" << device.GetPath() << "|" << deviceName << "|";
108 std::cout << device.GetHash() << std::endl;
109 }
110 }
111 outputFile_.close();
112 }
113 devices_.clear();
114 deviceEventBuffers_.clear();
115 PrintInfo("Recording stopped");
116 }
117
ProcessDeviceEvents(fd_set & readFds)118 void EventRecorder::ProcessDeviceEvents(fd_set& readFds)
119 {
120 for (InputDevice& device : devices_) {
121 if (!device.IsOpen()) {
122 continue;
123 }
124 int32_t fd = device.GetFd();
125 if (FD_ISSET(fd, &readFds)) {
126 input_event event;
127 if (device.ReadEvent(event)) {
128 EventRecord record;
129 record.deviceId = device.GetId();
130 record.event = event;
131 auto& currentDeviceBuffer = deviceEventBuffers_[record.deviceId];
132 currentDeviceBuffer.push_back(record);
133 FlushDeviceEvents(record);
134 }
135 }
136 }
137 }
138
MainLoop()139 void EventRecorder::MainLoop()
140 {
141 fd_set readFds;
142 struct timeval timeout;
143 PrintDebug("Started event recording main loop");
144 PrintInfo("Recording started. Press Ctrl+C to stop.");
145 while (running_ && !g_shutdown.load()) {
146 FD_ZERO(&readFds);
147 int32_t maxFd = -1;
148 for (const auto& device : devices_) {
149 if (device.IsOpen()) {
150 int32_t fd = device.GetFd();
151 FD_SET(fd, &readFds);
152 maxFd = std::max(maxFd, fd);
153 }
154 }
155 if (maxFd < 0) {
156 PrintError("No valid devices to monitor");
157 break;
158 }
159 timeout.tv_sec = 0;
160 timeout.tv_usec = TIME_OUT; // 100ms timeout
161 int32_t result = select(maxFd + 1, &readFds, nullptr, nullptr, &timeout);
162 if (result < 0) {
163 if (errno == EINTR) {
164 continue;
165 }
166 PrintError("Select error: %d", errno);
167 break;
168 }
169 if (result > 0) {
170 ProcessDeviceEvents(readFds);
171 }
172 }
173 PrintDebug("Stopped event recording main loop");
174 }
175
FlushDeviceEvents(const EventRecord & record)176 void EventRecorder::FlushDeviceEvents(const EventRecord& record)
177 {
178 if (record.event.type != EV_SYN || record.event.code != SYN_REPORT) {
179 return;
180 }
181 auto& currentDeviceBuffer = deviceEventBuffers_[record.deviceId];
182 for (const auto& record : currentDeviceBuffer) {
183 WriteEventText(record);
184 }
185 std::cout << std::endl;
186 currentDeviceBuffer.clear();
187 }
188
WriteEventText(const EventRecord & record)189 void EventRecorder::WriteEventText(const EventRecord& record)
190 {
191 struct input_event event = record.event;
192 std::string typeStr = GetEventTypeString(event.type);
193 std::string codeStr = GetEventCodeString(event.type, event.code);
194 outputFile_ << "["
195 << record.deviceId << ", "
196 << event.type << ", "
197 << event.code << ", "
198 << event.value<<", "
199 << event.input_event_sec << ", "
200 << event.input_event_usec
201 << "] # " << typeStr << " / " << codeStr << " " << event.value
202 << std::endl;
203 std::cout << "["
204 << record.deviceId << ", "
205 << event.type << ", "
206 << event.code << ", "
207 << event.value << ", "
208 << event.input_event_sec << ", "
209 << event.input_event_usec
210 << "] # " << typeStr << " / " << codeStr << " " << event.value
211 << std::endl;
212 }
213
GetEventTypeString(uint16_t type)214 std::string EventRecorder::GetEventTypeString(uint16_t type)
215 {
216 auto it = EVENT_TYPE_MAP.find(type);
217 if (it != EVENT_TYPE_MAP.end()) {
218 return it->second;
219 }
220 return "UNKNOWN_TYPE(" + std::to_string(type) + ")";
221 }
222
GetSecondaryEventCodeString(uint16_t type,uint16_t code)223 std::string EventRecorder::GetSecondaryEventCodeString(uint16_t type, uint16_t code)
224 {
225 switch (type) {
226 case EV_LED: {
227 auto it = LED_CODE_MAP.find(code);
228 if (it != LED_CODE_MAP.end()) {
229 return it->second;
230 }
231 break;
232 }
233 case EV_REP: {
234 auto it = REP_CODE_MAP.find(code);
235 if (it != REP_CODE_MAP.end()) {
236 return it->second;
237 }
238 break;
239 }
240 case EV_SND: {
241 auto it = SND_CODE_MAP.find(code);
242 if (it != SND_CODE_MAP.end()) {
243 return it->second;
244 }
245 break;
246 }
247 case EV_MSC: {
248 auto it = MSC_CODE_MAP.find(code);
249 if (it != MSC_CODE_MAP.end()) {
250 return it->second;
251 }
252 break;
253 }
254 case EV_SW: {
255 auto it = SW_CODE_MAP.find(code);
256 if (it != SW_CODE_MAP.end()) {
257 return it->second;
258 }
259 break;
260 }
261 }
262 return "";
263 }
264
GetEventCodeString(uint16_t type,uint16_t code)265 std::string EventRecorder::GetEventCodeString(uint16_t type, uint16_t code)
266 {
267 switch (type) {
268 case EV_SYN: {
269 auto it = SYN_CODE_MAP.find(code);
270 if (it != SYN_CODE_MAP.end()) {
271 return it->second;
272 }
273 break;
274 }
275 case EV_KEY: {
276 auto it = KEY_CODE_MAP.find(code);
277 if (it != KEY_CODE_MAP.end()) {
278 return it->second;
279 }
280 if (code >= KEY_A && code <= KEY_Z) {
281 return "KEY_" + std::string(1, 'A' + (code - KEY_A));
282 }
283 break;
284 }
285 case EV_REL: {
286 auto it = REL_CODE_MAP.find(code);
287 if (it != REL_CODE_MAP.end()) {
288 return it->second;
289 }
290 break;
291 }
292 case EV_ABS: {
293 auto it = ABS_CODE_MAP.find(code);
294 if (it != ABS_CODE_MAP.end()) {
295 return it->second;
296 }
297 break;
298 }
299 }
300 std::string secondaryResult = GetSecondaryEventCodeString(type, code);
301 if (!secondaryResult.empty()) {
302 return secondaryResult;
303 }
304 return "CODE(" + std::to_string(code) + ")";
305 }
306 } // namespace MMI
307 } // namespace OHOS