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_device.h"
17
18 #include <charconv>
19 #include <cstring>
20 #include <sstream>
21 #include <unistd.h>
22
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25
26 namespace OHOS {
27 namespace MMI {
28 namespace {
29 constexpr char DEVICE_PREFIX[] = "DEVICE:";
30 constexpr char FIELD_SEPARATOR = '|';
31 constexpr int32_t MAX_DEVICE_NAME = 128;
32 constexpr int32_t UINT_BIT_COUNT = 8;
33 constexpr int32_t DEVICE_DESCRIPTION_FILED_COUNT = 4;
34 }
35
InputDevice()36 InputDevice::InputDevice() : id_(0), fd_(-1)
37 {
38 }
39
InputDevice(const std::string & path,uint32_t id)40 InputDevice::InputDevice(const std::string& path, uint32_t id)
41 : path_(path), id_(id), fd_(-1)
42 {
43 OpenDevice(O_RDONLY | O_NONBLOCK);
44 if (fd_ >= 0) {
45 QueryDeviceInfo();
46 }
47 }
48
InputDevice(InputDevice && other)49 InputDevice::InputDevice(InputDevice&& other) noexcept
50 : path_(std::move(other.path_)),
51 name_(std::move(other.name_)),
52 hash_(std::move(other.hash_)),
53 id_(other.id_),
54 fd_(other.fd_)
55 {
56 other.fd_ = -1;
57 }
58
~InputDevice()59 InputDevice::~InputDevice()
60 {
61 Close();
62 }
63
operator =(InputDevice && other)64 InputDevice& InputDevice::operator=(InputDevice&& other) noexcept
65 {
66 if (this != &other) {
67 Close();
68 path_ = std::move(other.path_);
69 name_ = std::move(other.name_);
70 hash_ = std::move(other.hash_);
71 id_ = other.id_;
72 fd_ = other.fd_;
73 other.fd_ = -1;
74 }
75 return *this;
76 }
77
IsOpen() const78 bool InputDevice::IsOpen() const
79 {
80 return fd_ >= 0;
81 }
82
Close()83 void InputDevice::Close()
84 {
85 if (fd_ >= 0) {
86 ::close(fd_);
87 fd_ = -1;
88 }
89 }
90
OpenForReading()91 bool InputDevice::OpenForReading()
92 {
93 return OpenDevice(O_RDONLY | O_NONBLOCK);
94 }
95
OpenForWriting()96 bool InputDevice::OpenForWriting()
97 {
98 if (!VerifyDeviceMatch()) {
99 return false;
100 }
101 return OpenDevice(O_WRONLY);
102 }
103
GetFd() const104 int32_t InputDevice::GetFd() const
105 {
106 return fd_;
107 }
108
GetPath() const109 const std::string& InputDevice::GetPath() const
110 {
111 return path_;
112 }
113
GetName() const114 const std::string& InputDevice::GetName() const
115 {
116 return name_;
117 }
118
GetId() const119 uint32_t InputDevice::GetId() const
120 {
121 return id_;
122 }
123
GetHash() const124 std::string InputDevice::GetHash() const
125 {
126 return hash_;
127 }
128
SetId(uint32_t id)129 void InputDevice::SetId(uint32_t id)
130 {
131 id_ = id;
132 }
133
SetPath(const std::string & path)134 void InputDevice::SetPath(const std::string& path)
135 {
136 path_ = path;
137 }
138
SetName(const std::string & name)139 void InputDevice::SetName(const std::string& name)
140 {
141 name_ = name;
142 }
143
ReadEvent(input_event & event)144 bool InputDevice::ReadEvent(input_event& event)
145 {
146 if (fd_ < 0) {
147 return false;
148 }
149 ssize_t bytesRead = read(fd_, &event, sizeof(event));
150 return bytesRead == sizeof(event);
151 }
152
WriteEvents(const std::vector<input_event> & events)153 bool InputDevice::WriteEvents(const std::vector<input_event>& events)
154 {
155 if (fd_ < 0 || events.empty()) {
156 return false;
157 }
158 ssize_t eventBytes = static_cast<ssize_t>(sizeof(input_event) * events.size());
159 ssize_t bytesWritten = write(fd_, &events[0], eventBytes);
160 return bytesWritten == eventBytes;
161 }
162
VerifyDeviceMatch() const163 bool InputDevice::VerifyDeviceMatch() const
164 {
165 if (name_.empty()) {
166 return true;
167 }
168
169 // ensure the out coming path safe
170 char resolvedPath[PATH_MAX] = {};
171 if (realpath(path_.c_str(), resolvedPath) == nullptr) {
172 PrintError("Realpath failed. path:%{private}s", path_.c_str());
173 return false;
174 }
175
176 int32_t tempFd = ::open(resolvedPath, O_RDONLY | O_NONBLOCK);
177 if (tempFd < 0) {
178 PrintWarning("Cannot verify device %s: %s", path_.c_str(), strerror(errno));
179 return true;
180 }
181 bool matches = false;
182 char currentDeviceName[MAX_DEVICE_NAME] = "Unknown";
183 if (ioctl(tempFd, EVIOCGNAME(sizeof(currentDeviceName)), currentDeviceName) >= 0) {
184 if (name_ == currentDeviceName) {
185 matches = true;
186 } else {
187 PrintError("Device name mismatch for %s: expected '%s' but got '%s'",
188 path_.c_str(), name_.c_str(), currentDeviceName);
189 }
190 } else {
191 PrintWarning("Could not get device name for verification: %s", strerror(errno));
192 matches = true;
193 }
194 ::close(tempFd);
195 return matches;
196 }
197
OpenDevice(int32_t flags)198 bool InputDevice::OpenDevice(int32_t flags)
199 {
200 Close(); // Close if already open
201
202 // ensure the out coming path safe
203 char resolvedPath[PATH_MAX] = {};
204 if (realpath(path_.c_str(), resolvedPath) == nullptr) {
205 PrintError("Realpath failed. path:%{private}s", path_.c_str());
206 return false;
207 }
208
209 fd_ = ::open(resolvedPath, flags);
210 if (fd_ < 0) {
211 PrintError("Failed to open device %s: %s", path_.c_str(), strerror(errno));
212 return false;
213 }
214 return true;
215 }
216
QueryDeviceInfo()217 void InputDevice::QueryDeviceInfo()
218 {
219 char name[MAX_DEVICE_NAME] = "Unknown";
220 if (ioctl(fd_, EVIOCGNAME(sizeof(name)), name) >= 0) {
221 name_ = name;
222 }
223 CalculateDeviceHash();
224 }
225
CalculateDeviceHash()226 void InputDevice::CalculateDeviceHash()
227 {
228 std::ostringstream hashInput;
229 hashInput << "Name:" << name_ << "|";
230 struct input_id device_id;
231 if (ioctl(fd_, EVIOCGID, &device_id) >= 0) {
232 hashInput << "BusType:" << std::hex << device_id.bustype << "|"
233 << "Vendor:" << std::hex << device_id.vendor << "|"
234 << "Product:" << std::hex << device_id.product << "|"
235 << "Version:" << std::hex << device_id.version << "|";
236 }
237 unsigned long eventBits[EV_MAX/UINT_BIT_COUNT + 1] = {0};
238 if (ioctl(fd_, EVIOCGBIT(0, sizeof(eventBits)), eventBits) >= 0) {
239 hashInput << "Events:";
240 for (int i = 0; i <= EV_MAX; i++) {
241 if (eventBits[i / UINT_BIT_COUNT] & (1UL << (i % UINT_BIT_COUNT))) {
242 hashInput << std::hex << i << ",";
243 }
244 }
245 }
246 std::string hashStr = hashInput.str();
247 if (hashStr.back() == ',') {
248 hashStr.pop_back();
249 }
250 hash_ = std::to_string(std::hash<std::string>{}(hashStr));
251 }
252
InitFromTextLine(const std::string & line)253 bool InputDevice::InitFromTextLine(const std::string& line)
254 {
255 std::string workLine = line;
256 if (!RemovePrefix(workLine, DEVICE_PREFIX)) {
257 PrintError("Invalid device line format: %s", line.c_str());
258 return false;
259 }
260 std::vector<std::string> fields;
261 size_t pos = 0;
262 while ((pos = workLine.find(FIELD_SEPARATOR)) != std::string::npos) {
263 fields.push_back(workLine.substr(0, pos));
264 workLine.erase(0, pos + 1);
265 }
266 fields.push_back(workLine);
267 if (fields.size() != DEVICE_DESCRIPTION_FILED_COUNT) {
268 PrintError("Device line must have exactly 4 fields: %s", line.c_str());
269 return false;
270 }
271 int32_t index = -1;
272 TrimString(fields[++index]);
273 uint32_t deviceId;
274 auto result = std::from_chars(fields[index].data(), fields[index].data() + fields[index].size(), deviceId);
275 if (result.ec != std::errc()) {
276 PrintError("Invalid device ID: %s", fields[0].c_str());
277 return false;
278 }
279 TrimString(fields[++index]);
280 std::string devicePath = fields[index];
281 TrimString(fields[++index]);
282 std::string deviceName = fields[index];
283 TrimString(fields[++index]);
284 std::string deviceHash = fields[index];
285 id_ = deviceId;
286 path_ = devicePath;
287 name_ = deviceName;
288 hash_ = deviceHash;
289 return true;
290 }
291 } // namespace MMI
292 } // namespace OHOS