• 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_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