1 /*
2 * Copyright (c) 2023 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 "hotplug_detector.h"
17
18 #include <string>
19 #include <system_error>
20 #include <thread>
21
22 #include <dirent.h>
23 #include <sys/inotify.h>
24 #include <unistd.h>
25
26 #include "mmi_log.h"
27
28 namespace OHOS {
29 namespace MMI {
30 namespace {
31 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, MMI_LOG_DOMAIN, "HotplugDetector"};
32 constexpr auto MAX_EVENT_BUF_SIZE = 512;
33 constexpr auto INPUT_DEVICES_PATH = "/dev/input/";
34
SystemError()35 auto SystemError()
36 {
37 return std::error_code{errno, std::system_category()};
38 }
39 } // namespace
40
41 HotplugDetector::HotplugDetector() = default;
42
43 HotplugDetector::~HotplugDetector() = default;
44
Stop()45 void HotplugDetector::Stop()
46 {
47 CALL_DEBUG_ENTER;
48 inotifyFd_ = {};
49 }
50
Init(const callback & addFunc,const callback & removeFunc)51 bool HotplugDetector::Init(const callback& addFunc, const callback& removeFunc)
52 {
53 CALL_DEBUG_ENTER;
54 if (!addFunc || !removeFunc) {
55 return false;
56 }
57 addFunc_ = addFunc;
58 removeFunc_ = removeFunc;
59
60 auto fd = UniqueFd{inotify_init1(IN_CLOEXEC)};
61 if (fd < 0) {
62 MMI_HILOGE("Failed to initialize inotify. Error:%{public}s.", SystemError().message().c_str());
63 return false;
64 }
65 if (inotify_add_watch(fd, INPUT_DEVICES_PATH, IN_DELETE | IN_CREATE) < 0) {
66 MMI_HILOGE("Failed to add watch for input devices. Error:%{public}s.", SystemError().message().c_str());
67 return false;
68 }
69 if (!Scan()) {
70 return false;
71 }
72 inotifyFd_ = std::move(fd);
73 return true;
74 }
75
Scan() const76 bool HotplugDetector::Scan() const
77 {
78 CALL_DEBUG_ENTER;
79 using namespace std::literals::string_literals;
80 auto* dir = opendir(INPUT_DEVICES_PATH);
81 if (dir == nullptr) {
82 MMI_HILOGE("Failed to open device input dir. Error:%{public}s.", SystemError().message().c_str());
83 return false;
84 }
85 dirent* entry = nullptr;
86 while ((entry = readdir(dir)) != nullptr) {
87 if (entry->d_name == "."s || entry->d_name == ".."s) {
88 continue;
89 }
90 addFunc_(std::string{INPUT_DEVICES_PATH} + entry->d_name);
91 }
92 closedir(dir);
93 return true;
94 }
95
OnEvent() const96 void HotplugDetector::OnEvent() const
97 {
98 constexpr int32_t EVSIZE = static_cast<int32_t>(sizeof(inotify_event));
99 CALL_DEBUG_ENTER;
100 if (inotifyFd_ < 0) {
101 return;
102 }
103 std::byte event_buf[MAX_EVENT_BUF_SIZE];
104 int32_t res = read(inotifyFd_, event_buf, sizeof(event_buf));
105 if (res < EVSIZE) {
106 auto err = SystemError();
107 if (err != std::errc::resource_unavailable_try_again) {
108 MMI_HILOGE("Filed to read inotify event. Error:%{public}s.", err.message().c_str());
109 }
110 return;
111 }
112 inotify_event event;
113 for (int32_t pos = 0; res > EVSIZE;) {
114 std::copy_n(event_buf + pos, sizeof(event), reinterpret_cast<std::byte*>(&event));
115 if (event.len != 0) {
116 auto path = INPUT_DEVICES_PATH + std::string{reinterpret_cast<char*>(event_buf + pos + sizeof(event))};
117 if (event.mask & IN_CREATE) {
118 addFunc_(path);
119 } else {
120 removeFunc_(path);
121 }
122 }
123 int32_t consumed = EVSIZE + event.len;
124 pos += consumed;
125 res -= consumed;
126 }
127 }
128 } // namespace MMI
129 } // namespace OHOS
130