1 /*
2 * Copyright (c) 2022-2024 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_display_bind_helper.h"
17
18 #include <fstream>
19
20 #include "parameters.h"
21 #include "util.h"
22
23 #undef MMI_LOG_DOMAIN
24 #define MMI_LOG_DOMAIN MMI_LOG_WINDOW
25 #undef MMI_LOG_TAG
26 #define MMI_LOG_TAG "InputDisplayBindHelper"
27
28 namespace OHOS {
29 namespace MMI {
30 namespace {
31 const std::string FOLD_SCREEN_FLAG = system::GetParameter("const.window.foldscreen.type", "");
32 const char* INPUT_DEVICE_NAME_CONFIG { "/sys_prod/etc/input/input_device_name.cfg" };
33 const std::string DIRECTORY { "/sys/devices/virtual/input" };
34 const char* SEPARATOR { "/" };
35 const char* SUFFIX { "0000:0000" };
36 const std::string INPUT { "input" };
37 const std::string EVENT { "event" };
38 const char* NAME { "name" };
39 const int32_t DISPLAY_ID_MAIN { 0 };
40 const int32_t DISPLAY_ID_SUB { 5 };
41 }
42
43 namespace fs = std::filesystem;
44
IsDualDisplayFoldDevice()45 static bool IsDualDisplayFoldDevice()
46 {
47 return (!FOLD_SCREEN_FLAG.empty() && (FOLD_SCREEN_FLAG[0] == '2' || FOLD_SCREEN_FLAG[0] == '4'));
48 }
49
GetInputDeviceId() const50 int32_t BindInfo::GetInputDeviceId() const
51 {
52 return inputDeviceId_;
53 }
54
GetInputDeviceName() const55 std::string BindInfo::GetInputDeviceName() const
56 {
57 return inputDeviceName_;
58 }
59
GetDisplayId() const60 int32_t BindInfo::GetDisplayId() const
61 {
62 return displayId_;
63 }
64
GetDisplayName() const65 std::string BindInfo::GetDisplayName() const
66 {
67 return displayName_;
68 }
69
IsUnbind() const70 bool BindInfo::IsUnbind() const
71 {
72 return ((inputDeviceId_ == -1) || (displayId_ == -1));
73 }
74
InputDeviceNotBind() const75 bool BindInfo::InputDeviceNotBind() const
76 {
77 return (inputDeviceId_ == -1);
78 }
79
DisplayNotBind() const80 bool BindInfo::DisplayNotBind() const
81 {
82 return (displayId_ == -1);
83 }
84
AddInputDevice(int32_t deviceId,const std::string & deviceName)85 bool BindInfo::AddInputDevice(int32_t deviceId, const std::string &deviceName)
86 {
87 if ((inputDeviceId_ != -1) || !inputDeviceName_.empty()) {
88 return false;
89 }
90 inputDeviceId_ = deviceId;
91 inputDeviceName_ = deviceName;
92 return true;
93 }
94
RemoveInputDevice()95 void BindInfo::RemoveInputDevice()
96 {
97 inputDeviceId_ = -1;
98 inputDeviceName_.clear();
99 }
100
AddDisplay(int32_t id,const std::string & name)101 bool BindInfo::AddDisplay(int32_t id, const std::string &name)
102 {
103 if ((displayId_ != -1) || !displayName_.empty()) {
104 return false;
105 }
106 displayId_ = id;
107 displayName_ = name;
108 return true;
109 }
110
RemoveDisplay()111 void BindInfo::RemoveDisplay()
112 {
113 displayId_ = -1;
114 displayName_.clear();
115 }
116
GetDesc() const117 std::string BindInfo::GetDesc() const
118 {
119 std::ostringstream oss;
120 oss << "InputDevice(id:" << inputDeviceId_ << ",name:" << inputDeviceName_ << "),Display(id:" << displayId_ <<
121 ",name:" << displayName_ << ")";
122 return oss.str();
123 }
124
operator <(const BindInfo & l,const BindInfo & r)125 bool operator < (const BindInfo &l, const BindInfo &r)
126 {
127 if (l.inputDeviceId_ != r.inputDeviceId_) {
128 return (l.inputDeviceId_ < r.inputDeviceId_);
129 }
130 return (l.displayId_ < r.displayId_);
131 }
132
operator <<(std::ostream & os,const BindInfo & r)133 std::ostream &operator << (std::ostream &os, const BindInfo &r)
134 {
135 os << r.inputDeviceName_ << "<=>" << r.displayName_ << std::endl;
136 return os;
137 }
138
operator >>(std::istream & is,BindInfo & r)139 std::istream &operator >> (std::istream &is, BindInfo &r)
140 {
141 std::string line;
142 std::getline(is, line);
143 const std::string delim = "<=>";
144 std::string::size_type pos = line.find(delim);
145 if (pos == std::string::npos) {
146 return is;
147 }
148 r.inputDeviceName_ = line.substr(0, pos);
149 r.displayName_ = line.substr(pos + delim.length());
150 r.inputDeviceId_ = 0;
151 r.displayId_ = 0;
152 return is;
153 }
154
GetDesc() const155 std::string BindInfos::GetDesc() const
156 {
157 int32_t index = 0;
158 std::ostringstream oss;
159 for (const auto &info : infos_) {
160 oss << "index:" << index << "," << info.GetDesc() << std::endl;
161 }
162 return oss.str();
163 }
164
GetInfos() const165 const std::list<BindInfo> &BindInfos::GetInfos() const
166 {
167 return infos_;
168 }
169
GetBindDisplayIdByInputDevice(int32_t inputDeviceId) const170 int32_t BindInfos::GetBindDisplayIdByInputDevice(int32_t inputDeviceId) const
171 {
172 for (const auto &item : infos_) {
173 if (item.GetInputDeviceId() == inputDeviceId) {
174 if (!item.IsUnbind()) {
175 return item.GetDisplayId();
176 }
177 }
178 }
179 return -1;
180 }
181
GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const182 std::string BindInfos::GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const
183 {
184 for (const auto &item : infos_) {
185 if (item.GetInputDeviceId() == inputDeviceId) {
186 if (!item.IsUnbind()) {
187 return item.GetDisplayName();
188 }
189 }
190 }
191 return "";
192 }
193
GetDisplayNameByInputDevice(const std::string & name) const194 std::string BindInfos::GetDisplayNameByInputDevice(const std::string &name) const
195 {
196 for (const auto &item : infos_) {
197 if (item.GetInputDeviceName() == name) {
198 return item.GetDisplayName();
199 }
200 }
201 return "";
202 }
203
GetInputDeviceByDisplayName(const std::string & name) const204 std::string BindInfos::GetInputDeviceByDisplayName(const std::string &name) const
205 {
206 for (const auto &item : infos_) {
207 if (item.GetDisplayName() == name) {
208 return item.GetInputDeviceName();
209 }
210 }
211 return "";
212 }
213
Add(const BindInfo & info)214 bool BindInfos::Add(const BindInfo &info)
215 {
216 auto it = infos_.begin();
217 for (; it != infos_.end(); ++it) {
218 if (info < *it) {
219 break;
220 }
221 }
222 auto it2 = infos_.emplace(it, std::move(info));
223 if (it2 == infos_.end()) {
224 MMI_HILOGE("Duplicate %{public}s", info.GetDesc().c_str());
225 }
226 return true;
227 }
228
UnbindInputDevice(int32_t deviceId)229 void BindInfos::UnbindInputDevice(int32_t deviceId)
230 {
231 auto it = infos_.begin();
232 for (; it != infos_.end(); ++it) {
233 if (it->GetInputDeviceId() == deviceId) {
234 it->RemoveInputDevice();
235 infos_.erase(it);
236 return;
237 }
238 }
239 }
240
UnbindDisplay(int32_t displayId)241 void BindInfos::UnbindDisplay(int32_t displayId)
242 {
243 auto it = infos_.begin();
244 for (; it != infos_.end(); ++it) {
245 if (it->GetDisplayId() == displayId) {
246 it->RemoveDisplay();
247 infos_.erase(it);
248 return;
249 }
250 }
251 }
252
GetUnbindInputDevice(const std::string & displayName)253 BindInfo BindInfos::GetUnbindInputDevice(const std::string &displayName)
254 {
255 auto it = infos_.begin();
256 while (it != infos_.end()) {
257 if (it->InputDeviceNotBind()) {
258 if (it->GetDisplayName() == displayName) {
259 auto info = std::move(*it);
260 infos_.erase(it);
261 return info;
262 }
263 }
264 ++it;
265 }
266 return BindInfo();
267 }
268
GetUnbindDisplay()269 BindInfo BindInfos::GetUnbindDisplay()
270 {
271 auto it = infos_.begin();
272 while (it != infos_.end()) {
273 if (it->DisplayNotBind()) {
274 auto info = std::move(*it);
275 infos_.erase(it);
276 return info;
277 }
278 ++it;
279 }
280 return BindInfo();
281 }
282
GetUnbindDisplay(const std::string & inputDeviceName)283 BindInfo BindInfos::GetUnbindDisplay(const std::string &inputDeviceName)
284 {
285 auto it = infos_.begin();
286 while (it != infos_.end()) {
287 if (it->DisplayNotBind()) {
288 if (it->GetInputDeviceName() == inputDeviceName) {
289 auto info = std::move(*it);
290 infos_.erase(it);
291 return info;
292 }
293 }
294 ++it;
295 }
296 return GetUnbindDisplay();
297 }
298
operator <<(std::ostream & os,const BindInfos & r)299 std::ostream &operator << (std::ostream &os, const BindInfos &r)
300 {
301 const auto &infos = r.GetInfos();
302 for (const auto &info : infos) {
303 if (!info.IsUnbind()) {
304 os << info;
305 }
306 }
307 return os;
308 }
309
operator >>(std::istream & is,BindInfos & r)310 std::istream &operator >> (std::istream &is, BindInfos &r)
311 {
312 while (!is.eof()) {
313 BindInfo info;
314 is >> info;
315 if (info.IsUnbind()) {
316 break;
317 }
318 r.Add(info);
319 }
320 return is;
321 }
322
InputDisplayBindHelper(const std::string bindCfgFile)323 InputDisplayBindHelper::InputDisplayBindHelper(const std::string bindCfgFile)
324 : fileName_(bindCfgFile), infos_(std::make_shared<BindInfos>()), configFileInfos_(std::make_shared<BindInfos>())
325 {}
326
GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const327 std::string InputDisplayBindHelper::GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const
328 {
329 CALL_DEBUG_ENTER;
330 CHKPO(infos_);
331 return infos_->GetBindDisplayNameByInputDevice(inputDeviceId);
332 }
333
AddInputDevice(int32_t id,const std::string & name)334 void InputDisplayBindHelper::AddInputDevice(int32_t id, const std::string &name)
335 {
336 CALL_DEBUG_ENTER;
337 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
338 auto displayName = configFileInfos_->GetDisplayNameByInputDevice(name);
339 BindInfo info = infos_->GetUnbindInputDevice(displayName);
340 info.AddInputDevice(id, name);
341 infos_->Add(info);
342 Store();
343 }
344
RemoveInputDevice(int32_t id)345 void InputDisplayBindHelper::RemoveInputDevice(int32_t id)
346 {
347 CALL_DEBUG_ENTER;
348 MMI_HILOGD("Param: id:%{public}d", id);
349 infos_->UnbindInputDevice(id);
350 }
351
IsDisplayAdd(int32_t id,const std::string & name)352 bool InputDisplayBindHelper::IsDisplayAdd(int32_t id, const std::string &name)
353 {
354 const auto &infos = infos_->GetInfos();
355 for (const auto &info : infos) {
356 if ((info.GetDisplayName() == name) && (info.GetDisplayId() == id)) {
357 return true;
358 }
359 }
360 return false;
361 }
362
GetDisplayIdNames() const363 std::set<std::pair<int32_t, std::string>> InputDisplayBindHelper::GetDisplayIdNames() const
364 {
365 CALL_DEBUG_ENTER;
366 std::set<std::pair<int32_t, std::string>> idNames;
367 const auto &infos = infos_->GetInfos();
368 for (const auto &info : infos) {
369 if (info.GetDisplayId() != -1) {
370 idNames.insert(std::make_pair(info.GetDisplayId(), info.GetDisplayName()));
371 }
372 }
373 return idNames;
374 }
375
AddDisplay(int32_t id,const std::string & name)376 void InputDisplayBindHelper::AddDisplay(int32_t id, const std::string &name)
377 {
378 CALL_DEBUG_ENTER;
379 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
380 auto inputDeviceName = configFileInfos_->GetInputDeviceByDisplayName(name);
381 if (IsDualDisplayFoldDevice()) {
382 std::string deviceName = GetInputDeviceById(id);
383 if (!deviceName.empty()) {
384 inputDeviceName = deviceName;
385 }
386 }
387 BindInfo info = infos_->GetUnbindDisplay(inputDeviceName);
388 info.AddDisplay(id, name);
389 infos_->Add(info);
390 Store();
391 }
392
AddLocalDisplay(int32_t id,const std::string & name)393 void InputDisplayBindHelper::AddLocalDisplay(int32_t id, const std::string &name)
394 {
395 CALL_DEBUG_ENTER;
396 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
397 CHKPV(infos_);
398
399 const auto &infos = infos_->GetInfos();
400 std::vector<std::string> unbindDevices;
401 for (const auto &info : infos) {
402 if (info.DisplayNotBind()) {
403 unbindDevices.push_back(info.GetInputDeviceName());
404 MMI_HILOGI("Unbind InputDevice, id:%{public}d, inputDevice:%{public}s",
405 info.GetInputDeviceId(), info.GetInputDeviceName().c_str());
406 }
407 }
408
409 bool IsStore = false;
410 for (auto &item : unbindDevices) {
411 auto inputDeviceName = item;
412 if (IsDualDisplayFoldDevice()) {
413 std::string deviceName = GetInputDeviceById(id);
414 if (!deviceName.empty()) {
415 inputDeviceName = deviceName;
416 }
417 }
418 BindInfo info = infos_->GetUnbindDisplay(inputDeviceName);
419 info.AddDisplay(id, name);
420 infos_->Add(info);
421 IsStore = true;
422 }
423 if (IsStore) {
424 Store();
425 }
426 unbindDevices.clear();
427 }
428
GetInputDeviceById(int32_t id)429 std::string InputDisplayBindHelper::GetInputDeviceById(int32_t id)
430 {
431 CALL_DEBUG_ENTER;
432 if (id != DISPLAY_ID_MAIN && id != DISPLAY_ID_SUB) {
433 return "";
434 }
435
436 std::string inputNodeName = GetInputNodeNameByCfg(id);
437 if (inputNodeName.empty()) {
438 return "";
439 }
440
441 std::string inputNode = GetInputNode(inputNodeName);
442 if (inputNode.empty()) {
443 return "";
444 }
445
446 std::string inputEvent = inputNode;
447 size_t pos = inputEvent.find(INPUT);
448 if (pos != std::string::npos) {
449 inputEvent.replace(pos, INPUT.length(), EVENT);
450 }
451
452 std::string inputDeviceName;
453 inputDeviceName.append(DIRECTORY).append(SEPARATOR)
454 .append(inputNode).append(SEPARATOR)
455 .append(inputEvent).append(SUFFIX);
456
457 MMI_HILOGI("GetInputDeviceById, id:%{public}d, inputDevice:%{public}s", id, inputDeviceName.c_str());
458 return inputDeviceName;
459 }
460
GetInputNodeNameByCfg(int32_t id)461 std::string InputDisplayBindHelper::GetInputNodeNameByCfg(int32_t id)
462 {
463 CALL_DEBUG_ENTER;
464 std::ifstream file(INPUT_DEVICE_NAME_CONFIG);
465 std::string res;
466 if (file.is_open()) {
467 std::string line;
468 while (getline(file, line)) {
469 const std::string delim = "<=>";
470 size_t pos = line.find(delim);
471 if (pos == std::string::npos) {
472 continue;
473 }
474 std::string displayId = line.substr(0, pos);
475 std::string inputNodeName = line.substr(pos + delim.length());
476 if (!displayId.empty() && !inputNodeName.empty()
477 && std::all_of(displayId.begin(), displayId.end(), ::isdigit)
478 && std::atoi(displayId.c_str()) == id) {
479 res = inputNodeName;
480 break;
481 }
482 }
483 file.close();
484 }
485 if (!res.empty() && (res.back() == '\n' || res.back() == '\r')) {
486 res.pop_back();
487 }
488 return res;
489 }
490
GetContent(const std::string & fileName)491 std::string InputDisplayBindHelper::GetContent(const std::string &fileName)
492 {
493 CALL_DEBUG_ENTER;
494 std::string content;
495 char realPath[PATH_MAX] = {};
496 if (realpath(fileName.c_str(), realPath) == nullptr) {
497 MMI_HILOGE("The realpath return nullptr");
498 return content;
499 }
500 std::ifstream file(realPath);
501 if (file.is_open()) {
502 std::string line;
503 while (getline(file, line)) {
504 content.append(line);
505 }
506 file.close();
507 }
508 return content;
509 }
510
GetInputNode(const std::string & inputNodeName)511 std::string InputDisplayBindHelper::GetInputNode(const std::string &inputNodeName)
512 {
513 CALL_DEBUG_ENTER;
514 std::string inputNode;
515 if (fs::exists(DIRECTORY) && fs::is_directory(DIRECTORY)) {
516 for (const auto& entry : fs::directory_iterator(DIRECTORY)) {
517 std::string node = fs::path(entry.path()).filename();
518 std::string file;
519 file.append(DIRECTORY).append(SEPARATOR)
520 .append(node).append(SEPARATOR)
521 .append(NAME);
522 if (inputNodeName == GetContent(file)) {
523 inputNode = node;
524 break;
525 }
526 }
527 }
528 return inputNode;
529 }
530
RemoveDisplay(int32_t id)531 void InputDisplayBindHelper::RemoveDisplay(int32_t id)
532 {
533 CALL_DEBUG_ENTER;
534 MMI_HILOGD("Param: id:%{public}d", id);
535 infos_->UnbindDisplay(id);
536 }
537
Store()538 void InputDisplayBindHelper::Store()
539 {
540 CALL_DEBUG_ENTER;
541 CHKPV(infos_);
542 char realPath[PATH_MAX] = {};
543 CHKPV(realpath(fileName_.c_str(), realPath));
544 if (!IsValidJsonPath(realPath)) {
545 MMI_HILOGE("File path is invalid");
546 return;
547 }
548 std::ofstream ofs(realPath, std::ios::trunc | std::ios::out | std::ios_base::binary);
549 if (!ofs) {
550 MMI_HILOGE("Open file fail.%{public}s, errno:%{public}d", realPath, errno);
551 return;
552 }
553 ofs << *infos_;
554 ofs.close();
555 }
556
GetDisplayBindInfo(DisplayBindInfos & infos)557 int32_t InputDisplayBindHelper::GetDisplayBindInfo(DisplayBindInfos &infos)
558 {
559 CALL_DEBUG_ENTER;
560 CHKPR(infos_, RET_ERR);
561 for (const auto &item : infos_->GetInfos()) {
562 infos.push_back({
563 .inputDeviceId = item.GetInputDeviceId(),
564 .inputDeviceName = item.GetInputDeviceName(),
565 .displayId = item.GetDisplayId(),
566 .displayName = item.GetDisplayName(),
567 });
568 }
569 return RET_OK;
570 }
571
SetDisplayBind(int32_t deviceId,int32_t displayId,std::string & msg)572 int32_t InputDisplayBindHelper::SetDisplayBind(int32_t deviceId, int32_t displayId, std::string &msg)
573 {
574 CALL_DEBUG_ENTER;
575 MMI_HILOGD("Param: deviceId:%{public}d, displayId:%{public}d", deviceId, displayId);
576 if ((deviceId == -1) || (displayId == -1)) {
577 msg = "The deviceId or displayId is invalid";
578 MMI_HILOGE("%s", msg.c_str());
579 return RET_ERR;
580 }
581 if (infos_ == nullptr) {
582 msg = "Infos_ is nullptr";
583 MMI_HILOGE("%s", msg.c_str());
584 return RET_ERR;
585 }
586
587 BindInfo bindByDevice;
588 BindInfo bindByDisplay;
589 for (const auto &item : infos_->GetInfos()) {
590 if (item.GetInputDeviceId() == deviceId) {
591 bindByDevice = item;
592 }
593 if (item.GetDisplayId() == displayId) {
594 bindByDisplay = item;
595 }
596 }
597 if (bindByDevice.GetInputDeviceId() == -1) {
598 msg = "The deviceId is invalid";
599 MMI_HILOGE("%s", msg.c_str());
600 return RET_ERR;
601 }
602 if (bindByDisplay.GetDisplayId() == -1) {
603 msg = "The displayId is invalid";
604 MMI_HILOGE("%s", msg.c_str());
605 return RET_ERR;
606 }
607
608 if (infos_->GetBindDisplayIdByInputDevice(deviceId) == displayId) {
609 msg = "The input device and display has alread bind";
610 MMI_HILOGE("%s", msg.c_str());
611 return RET_ERR;
612 }
613
614 infos_->UnbindInputDevice(bindByDevice.GetInputDeviceId());
615 infos_->UnbindInputDevice(bindByDisplay.GetInputDeviceId());
616 infos_->UnbindDisplay(bindByDevice.GetDisplayId());
617 infos_->UnbindDisplay(bindByDisplay.GetDisplayId());
618
619 BindInfo info1;
620 info1.AddInputDevice(bindByDevice.GetInputDeviceId(), bindByDevice.GetInputDeviceName());
621 info1.AddDisplay(bindByDisplay.GetDisplayId(), bindByDisplay.GetDisplayName());
622 infos_->Add(info1);
623
624 if ((bindByDevice.GetDisplayId() != -1) && (bindByDisplay.GetInputDeviceId() != -1)) {
625 MMI_HILOGD("Both display id and input device id are invalid");
626 BindInfo info2;
627 info2.AddInputDevice(bindByDisplay.GetInputDeviceId(), bindByDisplay.GetInputDeviceName());
628 info2.AddDisplay(bindByDevice.GetDisplayId(), bindByDevice.GetDisplayName());
629 infos_->Add(info2);
630 return RET_OK;
631 }
632
633 if (bindByDevice.GetDisplayId() != -1) {
634 MMI_HILOGD("The display id is invalid");
635 AddDisplay(bindByDevice.GetDisplayId(), bindByDevice.GetDisplayName());
636 return RET_OK;
637 }
638
639 if (bindByDisplay.GetInputDeviceId() != -1) {
640 MMI_HILOGD("The input device id is invalid");
641 AddInputDevice(bindByDisplay.GetInputDeviceId(), bindByDisplay.GetInputDeviceName());
642 return RET_OK;
643 }
644
645 msg = "Can not reach here";
646 return RET_ERR;
647 }
648
Load()649 void InputDisplayBindHelper::Load()
650 {
651 CALL_DEBUG_ENTER;
652 char realPath[PATH_MAX] = {};
653 CHKPV(realpath(fileName_.c_str(), realPath));
654 if (!IsValidJsonPath(realPath)) {
655 MMI_HILOGE("The file path is invalid");
656 return;
657 }
658 std::ifstream ifs(realPath);
659 MMI_HILOGEK("Open file end:%{public}s", realPath);
660 if (!ifs) {
661 MMI_HILOGE("Open file fail.%{public}s, errno:%{public}d", realPath, errno);
662 return;
663 }
664 ifs >> *configFileInfos_;
665 ifs.close();
666 }
667
Dumps() const668 std::string InputDisplayBindHelper::Dumps() const
669 {
670 CALL_DEBUG_ENTER;
671 CHKPO(infos_);
672 std::ostringstream oss;
673 oss << *infos_;
674 return oss.str();
675 }
676 } // namespace MMI
677 } // namespace OHOS
678