• 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 "input_replay_command.h"
17 
18 #include <atomic>
19 #include <charconv>
20 #include <cstring>
21 #include <csignal>
22 #include <getopt.h>
23 #include <iostream>
24 #include <thread>
25 #include <unistd.h>
26 
27 #include "common.h"
28 #include "device_manager.h"
29 #include "event_recorder.h"
30 #include "event_replayer.h"
31 
32 namespace OHOS {
33 namespace MMI {
34 namespace {
35 constexpr int32_t MIN_ARGC = 2;
36 }
37 
38 std::atomic<bool> g_shutdown { false };
39 
InputReplayCommand(int32_t argc,char ** argv)40 InputReplayCommand::InputReplayCommand(int32_t argc, char** argv)
41     : argc_(argc), argv_(argv)
42 {
43     programName_ = (argc > 0) ? argv[0] : "uinput";
44 }
45 
ParseOptions()46 bool InputReplayCommand::ParseOptions()
47 {
48     static struct option longOptions[] = {
49         {"help", no_argument, 0, 'h'},
50         {"list", no_argument, 0, 'l'},
51         {"all", no_argument, 0, 'a'},
52         {"map", required_argument, 0, 'm'},
53         {0, 0, 0, 0}
54     };
55     int32_t opt;
56     int32_t optionIndex = 0;
57     optind = 1;
58     while ((opt = getopt_long(argc_, argv_, "hlam:", longOptions, &optionIndex)) != -1) {
59         switch (opt) {
60             case 'h':
61                 PrintUsage();
62                 exit(0);
63             case 'l':
64                 DeviceManager().PrintDeviceList();
65                 exit(0);
66             case 'a':
67                 useAllDevices_ = true;
68                 break;
69             case 'm':
70                 if (!ParseDeviceMapping(optarg)) {
71                     return false;
72                 }
73                 break;
74             default:
75                 return false;
76         }
77     }
78     return true;
79 }
80 
Parse()81 bool InputReplayCommand::Parse()
82 {
83     if (argc_ < MIN_ARGC) {
84         PrintUsage();
85         return false;
86     }
87     if (!ParseOptions()) {
88         return false;
89     }
90     if (optind >= argc_) {
91         PrintError("Missing command (record/replay)");
92         return false;
93     }
94     command_ = argv_[optind++];
95     if (optind >= argc_) {
96         PrintError("Missing file path");
97         return false;
98     }
99     filePath_ = argv_[optind++];
100     if (command_ == "record") {
101         return ParseRecordCommand();
102     } else if (command_ == "replay") {
103         return ParseReplayCommand();
104     } else {
105         PrintError("Invalid command, supported: record/replay");
106         return false;
107     }
108 }
109 
SignalHandler(int32_t sig)110 inline void SignalHandler(int32_t sig)
111 {
112     if (sig == SIGINT || sig == SIGTERM) {
113         g_shutdown.store(true);
114         std::cout << "\nShutdown signal received, cleaning up..." << std::endl;
115     }
116 }
117 
HandleRecordReplayCommand(int32_t argc,char ** argv)118 int32_t InputReplayCommand::HandleRecordReplayCommand(int32_t argc, char** argv)
119 {
120     OHOS::MMI::InputReplayCommand parser(argc, argv);
121     if (geteuid() != 0) {
122         std::cerr << "Error: This program must be run as root" << std::endl;
123         return RET_ERR;
124     }
125     if (!parser.Parse()) {
126         std::cerr << "Failed to parse record/replay command" << std::endl;
127         return RET_ERR;
128     }
129 
130     if (!parser.Execute()) {
131         return RET_ERR;
132     }
133     return RET_OK;
134 }
135 
Execute()136 bool InputReplayCommand::Execute()
137 {
138     if (command_ == "record") {
139         return ExecuteRecordCommand();
140     } else if (command_ == "replay") {
141         return ExecuteReplayCommand();
142     }
143     return false;
144 }
145 
ParseDeviceMapping(const std::string & mappingStr)146 bool InputReplayCommand::ParseDeviceMapping(const std::string& mappingStr)
147 {
148     deviceMapping_.clear();
149     const char* ptr = mappingStr.c_str();
150     const char* endPtr = ptr + mappingStr.length();
151     while (ptr < endPtr) {
152         uint16_t key;
153         auto keyResult = std::from_chars(ptr, endPtr, key);
154         if (keyResult.ec != std::errc() || keyResult.ptr >= endPtr || *keyResult.ptr != ':') {
155             return false;
156         }
157         ptr = keyResult.ptr + 1;
158         uint16_t value;
159         auto valueResult = std::from_chars(ptr, endPtr, value);
160         if (valueResult.ec != std::errc()) {
161             return false;
162         }
163         deviceMapping_[key] = value;
164         ptr = valueResult.ptr;
165         if (ptr < endPtr) {
166             if (*ptr != ',') {
167                 return false;
168             }
169             ptr++;
170         }
171     }
172     return !deviceMapping_.empty();
173 }
174 
SetupSignalHandlers()175 void InputReplayCommand::SetupSignalHandlers()
176 {
177     struct sigaction sa;
178     sa.sa_handler = SignalHandler;
179     sigemptyset(&sa.sa_mask);
180     sa.sa_flags = 0;
181     sigaction(SIGINT, &sa, nullptr);
182     sigaction(SIGTERM, &sa, nullptr);
183 }
184 
ParseRecordCommand()185 bool InputReplayCommand::ParseRecordCommand()
186 {
187     if (!useAllDevices_) {
188         while (optind < argc_) {
189             devicePaths_.push_back(argv_[optind++]);
190         }
191         if (devicePaths_.empty()) {
192             PrintError("No devices specified for recording");
193             PrintError("Use --all to record from all devices or specify device paths");
194             return false;
195         }
196     }
197     if (optind < argc_) {
198         PrintError("Unexpected arguments for record command");
199         return false;
200     }
201     return true;
202 }
203 
ParseReplayCommand()204 bool InputReplayCommand::ParseReplayCommand()
205 {
206     if (useAllDevices_) {
207         PrintError("Not use -a option for replay command!");
208         return false;
209     }
210     if (optind < argc_) {
211         PrintError("Unexpected arguments for replay command");
212         return false;
213     }
214     return true;
215 }
216 
ExecuteRecordCommand()217 bool InputReplayCommand::ExecuteRecordCommand()
218 {
219     std::vector<InputDevice> devices;
220     if (useAllDevices_) {
221         DeviceManager deviceManager;
222         devices = deviceManager.DiscoverDevices();
223     } else {
224         const std::string PREFIX = "/dev/input/event";
225         for (size_t i = 0; i < devicePaths_.size(); i++) {
226             const std::string& path = devicePaths_[i];
227             if (path.substr(0, PREFIX.length()) != PREFIX) {
228                 PrintError("Invalid input device path format: %s", path.c_str());
229                 return false;
230             }
231             uint16_t deviceId = 0;
232             const char* start = path.c_str() + PREFIX.length();
233             const char* end = path.c_str() + path.length();
234             std::from_chars_result result = std::from_chars(start, end, deviceId);
235             if (result.ec != std::errc() || result.ptr != end) {
236                 PrintError("Invalid device number in path: %s", path.c_str());
237                 return false;
238             }
239             InputDevice device(path, deviceId);
240             if (device.IsOpen()) {
241                 devices.push_back(std::move(device));
242             } else {
243                 PrintWarning("Failed to open device: %s", path.c_str());
244             }
245         }
246     }
247     if (devices.empty()) {
248         PrintError("No valid input devices specified");
249         return false;
250     }
251     SetupSignalHandlers();
252     EventRecorder recorder(filePath_);
253     if (!recorder.Start(devices)) {
254         return false;
255     }
256     recorder.Stop();
257     return true;
258 }
259 
ExecuteReplayCommand()260 bool InputReplayCommand::ExecuteReplayCommand()
261 {
262     SetupSignalHandlers();
263     PrintInfo("Press Enter to start replay...");
264     std::cin.get();
265     EventReplayer replayer(filePath_, deviceMapping_);
266     return replayer.Replay();
267 }
268 
PrintUsage() const269 void InputReplayCommand::PrintUsage() const
270 {
271     std::cout << "Usage:" << std::endl
272               << "  " << programName_ << " [options] record <output-file> [device paths...]" << std::endl
273               << "  " << programName_ << " [options] replay <input-file>" << std::endl
274               << std::endl
275               << "Options:" << std::endl
276               << "  -h, --help       Show this help message" << std::endl
277               << "  -l, --list       List available input devices" << std::endl
278               << "  -a, --all        Record from all available input devices" << std::endl
279               << "  -m, --map        Specify device mapping for replay (e.g., \"0:0,1:2,4:2\")" << std::endl
280               << "                   Format: sourceDeviceId:targetDeviceId,..." << std::endl
281               << std::endl
282               << "Examples:" << std::endl
283               << "  " << programName_ << " record -a events.bin           # Record from all devices" << std::endl
284               << "  " << programName_ << " record events.bin /dev/input/event0 /dev/input/event1  "
285                                          "# Record from specific devices" << std::endl
286               << "  " << programName_ << " replay events.bin              # Replay to original devices" << std::endl
287               << "  " << programName_
288               << " replay -m \"0:1,1:0\" events.bin # Replay with custom device mapping" << std::endl;
289 }
290 } // namespace MMI
291 } // namespace OHOS