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