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