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_replayer.h"
17
18 #include <charconv>
19 #include <chrono>
20 #include <fstream>
21 #include <thread>
22 #include <unordered_map>
23 #include <vector>
24
25 #include "common.h"
26 #include "device_manager.h"
27
28 namespace OHOS {
29 namespace MMI {
30 namespace {
31 constexpr int32_t MICROSECONDS_PER_SECOND = 1000000;
32 const char* DEVICES_PREFIX = "DEVICES:";
33 const char* EVENTS_BEGIN = "EVENTS_BEGIN";
34 const char* EVENTS_END = "EVENTS_END";
35 constexpr char FIELD_COMMA = ',';
36 constexpr char COMMENT_CHAR = '#';
37 constexpr char BRACKET_START = '[';
38 constexpr char BRACKET_END = ']';
39 }
40
EventReplayer(const std::string & inputPath,const std::map<uint16_t,uint16_t> & deviceMapping)41 EventReplayer::EventReplayer(const std::string& inputPath, const std::map<uint16_t, uint16_t>& deviceMapping)
42 : inputPath_(inputPath), lastSec_(0), lastUsec_(0), firstEvent_(true)
43 {
44 for (const auto &[sourceId, targetId] : deviceMapping) {
45 deviceMapping_[sourceId] = "/dev/input/event" + std::to_string(targetId);
46 }
47 DeviceManager deviceManager;
48 auto devices = deviceManager.DiscoverDevices();
49 for (const InputDevice& device : devices) {
50 hashToDevicePath_[device.GetHash()] = device.GetPath();
51 }
52 if (devices.size() != hashToDevicePath_.size()) {
53 PrintWarning("Device hashVal is not unique!");
54 }
55 }
56
SeekToDevicesSection(std::ifstream & inputFile)57 bool EventReplayer::SeekToDevicesSection(std::ifstream& inputFile)
58 {
59 if (!inputFile.good()) {
60 PrintError("File stream is not in good state");
61 return false;
62 }
63 inputFile.seekg(0, std::ios::end);
64 std::streampos fileSize = inputFile.tellg();
65 constexpr std::streamoff SEARCH_OFFSET = 2048;
66 if (fileSize <= SEARCH_OFFSET) {
67 inputFile.seekg(0, std::ios::beg);
68 } else {
69 inputFile.seekg(-SEARCH_OFFSET, std::ios::end);
70 std::string discardLine;
71 std::getline(inputFile, discardLine);
72 if (inputFile.eof()) {
73 inputFile.clear();
74 inputFile.seekg(0, std::ios::beg);
75 }
76 }
77 std::string line;
78 while (std::getline(inputFile, line)) {
79 if (line.find(DEVICES_PREFIX) == 0) {
80 inputFile.seekg(-static_cast<std::streamoff>(line.length() + 1), std::ios::cur);
81 if (inputFile.peek() == '\r') {
82 inputFile.seekg(-1, std::ios::cur);
83 }
84 return true;
85 }
86 }
87 PrintWarning("DEVICES section not found in file");
88 return false;
89 }
90
SeekToEventsSection(std::ifstream & inputFile)91 bool EventReplayer::SeekToEventsSection(std::ifstream& inputFile)
92 {
93 if (!inputFile.good()) {
94 PrintError("File stream is not in good state");
95 return false;
96 }
97 inputFile.seekg(0, std::ios::beg);
98 std::string line;
99 while (std::getline(inputFile, line)) {
100 if (line == EVENTS_BEGIN) {
101 return true;
102 }
103 }
104 return false;
105 }
106
Replay()107 bool EventReplayer::Replay()
108 {
109 // ensure the out coming path safe
110 char resolvedPath[PATH_MAX] = {};
111 if (realpath(inputPath_.c_str(), resolvedPath) == nullptr) {
112 PrintError("Realpath failed. path:%{private}s", inputPath_.c_str());
113 return false;
114 }
115 std::ifstream inputFile(resolvedPath);
116 if (!inputFile.is_open()) {
117 PrintError("Failed to open file, error:%{private}s", strerror(errno));
118 return false;
119 }
120 if (!SeekToDevicesSection(inputFile)) {
121 PrintError("seek to DEVICES_PREFIX tag error");
122 return false;
123 }
124 std::map<uint32_t, std::unique_ptr<InputDevice>> outputDevices;
125 if (!InitializeOutputDevices(inputFile, outputDevices)) {
126 return false;
127 }
128 if (outputDevices.empty()) {
129 PrintError("No output devices available for replay");
130 return false;
131 }
132 PrintInfo("Starting replay...");
133 firstEvent_ = true;
134 bool result = ReplayEvents(inputFile, outputDevices);
135 PrintInfo(result ? "Replay completed" : "Replay interrupted");
136 return true;
137 }
138
ProcessDeviceLines(std::ifstream & inputFile,std::map<uint32_t,std::unique_ptr<InputDevice>> & outputDevices,uint32_t deviceCount)139 bool EventReplayer::ProcessDeviceLines(std::ifstream& inputFile,
140 std::map<uint32_t, std::unique_ptr<InputDevice>>& outputDevices, uint32_t deviceCount)
141 {
142 std::string line;
143 for (uint32_t i = 0; i < deviceCount; i++) {
144 line.clear();
145 if (!std::getline(inputFile, line)) {
146 PrintWarning("Reached end of file after reading %u of %u devices", i, deviceCount);
147 break;
148 }
149 if (line.empty() || line[0] == COMMENT_CHAR) {
150 --i;
151 continue;
152 }
153 auto device = std::make_unique<InputDevice>();
154 if (!device) {
155 PrintError("Failed to allocate device object");
156 return false;
157 }
158 if (!device->InitFromTextLine(line)) {
159 PrintWarning("Failed to parse device line: %s", line.c_str());
160 continue;
161 }
162 uint16_t deviceId = static_cast<uint16_t>(device->GetId());
163 auto mappingIt = deviceMapping_.find(deviceId);
164 if (mappingIt != deviceMapping_.end()) {
165 PrintInfo("Mapping device %u to %s", deviceId, mappingIt->second.c_str());
166 device->SetPath(mappingIt->second);
167 }
168 if (deviceMapping_.empty()) {
169 std::string path = device->GetPath();
170 std::string hash = device->GetHash();
171 auto hashIt = hashToDevicePath_.find(hash);
172 if (hashIt != hashToDevicePath_.end() && hashToDevicePath_[hash] != path) {
173 PrintInfo("Validate and correct path using hash verification");
174 device->SetPath(hashToDevicePath_[hash]);
175 }
176 }
177 if (device->OpenForWriting()) {
178 PrintInfo("Using device %u: %s", device->GetId(), device->GetName().c_str());
179 outputDevices[device->GetId()] = std::move(device);
180 } else {
181 PrintWarning("Failed to open device for replay: %s", device->GetPath().c_str());
182 }
183 }
184 return true;
185 }
186
InitializeOutputDevices(std::ifstream & inputFile,std::map<uint32_t,std::unique_ptr<InputDevice>> & outputDevices)187 bool EventReplayer::InitializeOutputDevices(std::ifstream& inputFile,
188 std::map<uint32_t, std::unique_ptr<InputDevice>>& outputDevices)
189 {
190 outputDevices.clear();
191 std::string line;
192 if (!std::getline(inputFile, line)) {
193 PrintError("Failed to read device count line");
194 return false;
195 }
196 std::string countStr = line;
197 if (!RemovePrefix(countStr, DEVICES_PREFIX)) {
198 PrintError("Invalid device count line format");
199 return false;
200 }
201 TrimString(countStr);
202 uint32_t deviceCount = 0;
203 auto result = std::from_chars(countStr.data(), countStr.data() + countStr.size(), deviceCount);
204 if (result.ec != std::errc()) {
205 PrintError("Failed to parse device count: %s", countStr.c_str());
206 return false;
207 }
208 PrintInfo("Found %u devices", deviceCount);
209 return ProcessDeviceLines(inputFile, outputDevices, deviceCount);
210 }
211
ApplyEventDelay(const struct input_event & currentEvent)212 void EventReplayer::ApplyEventDelay(const struct input_event& currentEvent)
213 {
214 if (!firstEvent_) {
215 long nowSec = currentEvent.input_event_sec;
216 long nowUsec = currentEvent.input_event_usec;
217 long diffSec = nowSec - lastSec_;
218 long diffUsec = nowUsec - lastUsec_;
219 if (diffUsec < 0) {
220 diffSec--;
221 diffUsec += MICROSECONDS_PER_SECOND;
222 }
223 if (diffSec > 0 || diffUsec > 0) {
224 std::this_thread::sleep_for(
225 std::chrono::seconds(diffSec) + std::chrono::microseconds(diffUsec));
226 }
227 }
228 firstEvent_ = false;
229 lastSec_ = currentEvent.input_event_sec;
230 lastUsec_ = currentEvent.input_event_usec;
231 }
232
ReplayEvents(std::ifstream & inputFile,const std::map<uint32_t,std::unique_ptr<InputDevice>> & outputDevices)233 bool EventReplayer::ReplayEvents(std::ifstream& inputFile,
234 const std::map<uint32_t, std::unique_ptr<InputDevice>>& outputDevices)
235 {
236 if (!SeekToEventsSection(inputFile)) {
237 PrintError("Failed to locate events section");
238 return false;
239 }
240 deviceEventBuffers_.clear();
241 std::string line;
242 while (std::getline(inputFile, line)) {
243 if (!inputFile || inputFile.eof()) {
244 PrintWarning("Error reading event record");
245 return false;
246 }
247 if (line.empty()) {
248 continue;
249 }
250 if (line == EVENTS_END) {
251 PrintDebug("Reached end of events section");
252 break;
253 }
254 uint32_t deviceId;
255 input_event event;
256 if (!ParseInputLine(line, deviceId, event)) {
257 PrintError("Failed to parse event line: %s", line.c_str());
258 return false;
259 }
260 auto deviceIt = outputDevices.find(deviceId);
261 if (deviceIt == outputDevices.end()) {
262 continue;
263 }
264 ApplyEventDelay(event);
265 auto& currentDeviceBuffer = deviceEventBuffers_[deviceId];
266 currentDeviceBuffer.push_back(event);
267 if (event.type == EV_SYN && event.code == SYN_REPORT) {
268 if (!deviceIt->second->WriteEvents(currentDeviceBuffer)) {
269 PrintError("Failed to write events for device %u", deviceId);
270 return false;
271 }
272 currentDeviceBuffer.clear();
273 }
274 if (g_shutdown.load()) {
275 return false;
276 }
277 line.clear();
278 }
279 return true;
280 }
281
282 template<typename T>
ParseField(const char * & ptr,const char * endPtr,T & value)283 static bool ParseField(const char*& ptr, const char* endPtr, T& value)
284 {
285 while (ptr < endPtr && (*ptr == ' ' || *ptr == '\t')) {
286 ptr++;
287 }
288 auto result = std::from_chars(ptr, endPtr, value);
289 if (result.ec != std::errc()) {
290 return false;
291 }
292 ptr = result.ptr;
293 if (ptr >= endPtr) {
294 return false;
295 }
296 if (*ptr++ != FIELD_COMMA) {
297 return false;
298 }
299 return true;
300 }
301
ParseInputLine(const std::string & line,uint32_t & deviceId,struct input_event & evt)302 bool EventReplayer::ParseInputLine(const std::string& line, uint32_t& deviceId, struct input_event& evt)
303 {
304 size_t commentPos = line.find(COMMENT_CHAR);
305 std::string content = (commentPos != std::string::npos) ? line.substr(0, commentPos) : line;
306 size_t startPos = content.find(BRACKET_START);
307 size_t endPos = content.find(BRACKET_END);
308 if (startPos == std::string::npos || endPos == std::string::npos || startPos >= endPos) {
309 return false;
310 }
311 std::string data = content.substr(startPos + 1, endPos - startPos - 1);
312 const char* ptr = data.c_str();
313 const char* endPtr = ptr + data.length();
314 if (!ParseField(ptr, endPtr, deviceId)) {
315 return false;
316 }
317 uint16_t type;
318 if (!ParseField(ptr, endPtr, type)) {
319 return false;
320 }
321 evt.type = type;
322 uint16_t code;
323 if (!ParseField(ptr, endPtr, code)) {
324 return false;
325 }
326 evt.code = code;
327 int32_t value;
328 if (!ParseField(ptr, endPtr, value)) {
329 return false;
330 }
331 evt.value = value;
332 long s;
333 if (!ParseField(ptr, endPtr, s)) {
334 return false;
335 }
336 evt.input_event_sec = s;
337 long us;
338 while (ptr < endPtr && (*ptr == ' ' || *ptr == '\t')) {
339 ptr++;
340 }
341 auto result = std::from_chars(ptr, endPtr, us);
342 if (result.ec != std::errc()) {
343 return false;
344 }
345 evt.input_event_usec = us;
346 return true;
347 }
348 } // namespace MMI
349 } // namespace OHOS