• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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