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 "multimodal_input_plugin_manager.h"
17
18 #include "mmi_log.h"
19 #include <memory>
20
21 #undef MMI_LOG_DOMAIN
22 #define MMI_LOG_DOMAIN MMI_LOG_SERVER
23 #undef MMI_LOG_TAG
24 #define MMI_LOG_TAG "MultiModalInputPluginManager"
25
26 namespace OHOS {
27 namespace MMI {
28 std::shared_ptr<InputPluginManager> InputPluginManager::instance_;
29 std::once_flag InputPluginManager::init_flag_;
30
31 const char *FILE_EXTENSION = ".so";
32 const char *FOLDER_PATH = "/system/lib64/multimodalinput/autorun";
33 const int32_t TIMEOUT_US = 300;
34 const int32_t MAX_TIMER = 3;
35
~InputPluginManager()36 InputPluginManager::~InputPluginManager()
37 {
38 plugins_.clear();
39 if (instance_ != nullptr) {
40 instance_ = nullptr;
41 }
42 MMI_HILOGI("~InputPluginManager");
43 }
44
GetInstance(const std::string & directory)45 std::shared_ptr<InputPluginManager> InputPluginManager::GetInstance(const std::string &directory)
46 {
47 std::call_once(init_flag_, [&directory] {
48 if (instance_ == nullptr) {
49 MMI_HILOGI("New InputPluginManager");
50 std::string dir = directory.empty() ? FOLDER_PATH : directory;
51 instance_ = std::make_shared<InputPluginManager>(dir);
52 }
53 });
54 return instance_;
55 }
56
Init()57 int32_t InputPluginManager::Init()
58 {
59 CALL_DEBUG_ENTER;
60 DIR *dir = opendir(directory_.c_str());
61 if (!dir) {
62 MMI_HILOGE("Failed to open error:%{private}s", strerror(errno));
63 return RET_OK;
64 }
65 struct dirent *entry;
66 while ((entry = readdir(dir)) != nullptr) {
67 if (entry->d_type == DT_REG && std::string(entry->d_name) != "." && std::string(entry->d_name) != "..") {
68 std::string path = directory_ + "/" + entry->d_name;
69 if (path.length() >= strlen(FILE_EXTENSION) &&
70 path.substr(path.size() - strlen(FILE_EXTENSION)) == FILE_EXTENSION) {
71 LoadPlugin(path);
72 }
73 }
74 }
75 closedir(dir);
76 PrintPlugins();
77 return RET_OK;
78 }
79
LoadPlugin(const std::string & path)80 bool InputPluginManager::LoadPlugin(const std::string &path)
81 {
82 CALL_DEBUG_ENTER;
83 void *handle = dlopen(path.c_str(), RTLD_LAZY);
84 if (!handle) {
85 MMI_HILOGE("Failed to load directory: %{private}s", dlerror());
86 return false;
87 }
88
89 InitPlugin func = reinterpret_cast<InitPlugin>(dlsym(handle, "InitPlugin"));
90 if (!func) {
91 MMI_HILOGE("Failed to find symbol InitPlugin in: %{private}s", dlerror());
92 dlclose(handle);
93 return false;
94 }
95
96 std::shared_ptr<InputPlugin> cPin = std::make_shared<InputPlugin>();
97 if (!cPin) {
98 dlclose(handle);
99 return false;
100 }
101
102 cPin->unintPlugin_ = reinterpret_cast<UnintPlugin>(dlsym(handle, "UnintPlugin"));
103 if (!cPin->unintPlugin_) {
104 MMI_HILOGE("Failed to find symbol UnintPlugin in: %{private}s", dlerror());
105 dlclose(handle);
106 return false;
107 }
108 std::shared_ptr<IInputPlugin> iPin;
109 int32_t ret = func(cPin, iPin);
110 if (ret != 0 || !iPin) {
111 MMI_HILOGE("Failed to InitPlugin plugin.");
112 dlclose(handle);
113 return false;
114 }
115 ret = cPin->Init(iPin);
116 if (ret != 0) {
117 MMI_HILOGE("Failed to Init plugin.");
118 dlclose(handle);
119 return false;
120 }
121 cPin->handle_ = handle;
122 InputPluginStage stage = iPin->GetStage();
123
124 auto result = plugins_.insert({stage, {cPin}});
125 if (!result.second) {
126 auto it = std::lower_bound(result.first->second.begin(), result.first->second.end(), cPin,
127 [](const std::shared_ptr<InputPlugin> &a, const std::shared_ptr<InputPlugin> &b) {
128 return a->prio_ < b->prio_;
129 });
130 result.first->second.insert(it, cPin);
131 }
132 return true;
133 }
134
PrintPlugins()135 void InputPluginManager::PrintPlugins()
136 {
137 for (const auto &stagePlugins : plugins_) {
138 MMI_HILOGI("InputPluginManager InputPluginStage : %{public}d", stagePlugins.first);
139 for (const auto &plugin : stagePlugins.second) {
140 MMI_HILOGI("InputPluginManager : name:%{public}s prio_:%{public}d",
141 plugin->name_.c_str(), plugin->prio_);
142 }
143 }
144 }
145
PluginAssignmentCallBack(std::function<void (libinput_event *,int64_t)> callback,InputPluginStage stage)146 void InputPluginManager::PluginAssignmentCallBack(
147 std::function<void(libinput_event *, int64_t)> callback, InputPluginStage stage)
148 {
149 CALL_DEBUG_ENTER;
150 auto it = plugins_.find(stage);
151 if (it == plugins_.end()) {
152 MMI_HILOGI("plugins_ not stage:%{public}d.", stage);
153 return;
154 }
155 for (auto &plugin : it->second) {
156 plugin->callback_ = callback;
157 }
158 }
159
PluginAssignmentCallBack(std::function<void (std::shared_ptr<KeyEvent>)> callback,InputPluginStage stage)160 void InputPluginManager::PluginAssignmentCallBack(
161 std::function<void(std::shared_ptr<KeyEvent>)> callback, InputPluginStage stage)
162 {
163 CALL_DEBUG_ENTER;
164 auto it = plugins_.find(stage);
165 if (it == plugins_.end()) {
166 MMI_HILOGI("plugins_ not stage:%{public}d.", stage);
167 return;
168 }
169 for (auto &plugin : it->second) {
170 if (plugin != nullptr) {
171 plugin->keyEventCallback_ = callback;
172 }
173 }
174 }
175
HandleEvent(libinput_event * event,int64_t frameTime,InputPluginStage stage)176 int32_t InputPluginManager::HandleEvent(libinput_event *event, int64_t frameTime, InputPluginStage stage)
177 {
178 return DoHandleEvent(event, frameTime, nullptr, stage);
179 }
180
DoHandleEvent(libinput_event * event,int64_t frameTime,InputPlugin * iplugin,InputPluginStage stage)181 int32_t InputPluginManager::DoHandleEvent(
182 libinput_event *event, int64_t frameTime, InputPlugin *iplugin, InputPluginStage stage)
183 {
184 if (event == nullptr) {
185 return RET_NOTDO;
186 }
187 auto it = plugins_.find(stage);
188 if (it == plugins_.end()) {
189 return RET_NOTDO;
190 }
191 CALL_DEBUG_ENTER;
192 auto &plugins = it->second;
193 auto start_plugin = plugins.begin();
194 if (iplugin != nullptr) {
195 auto cur_plugin = std::find_if(plugins.begin(), plugins.end(),
196 [iplugin](const std::shared_ptr<InputPlugin> &plugin) { return plugin.get() == iplugin; });
197 if (cur_plugin == plugins.end()) {
198 return RET_NOTDO;
199 }
200 start_plugin = std::next(cur_plugin);
201 }
202 int64_t beginTime = 0;
203 PluginResult result;
204 int64_t endTime = 0;
205 int64_t lostTime = 0;
206 for (auto pluginIt = start_plugin; pluginIt != plugins.end(); ++pluginIt) {
207 if ((*pluginIt) == nullptr) {
208 continue;
209 }
210 beginTime = GetSysClockTime();
211 result = (*pluginIt)->HandleEvent(event, frameTime);
212 endTime = GetSysClockTime();
213 lostTime = endTime - beginTime;
214 if (lostTime >= TIMEOUT_US) {
215 MMI_HILOGE("pluginIt timeout name:%{public}s ,endTime:%{public}" PRId64 ",lostTime:%{public}" PRId64,
216 (*pluginIt)->name_.c_str(), endTime, lostTime);
217 }
218 if (result == PluginResult::UseNeedReissue) {
219 if (IntermediateEndEvent(event)) {
220 MMI_HILOGE("pluginIt is intermediate or end event");
221 continue;
222 }
223 return RET_DO;
224 } else if (result == PluginResult::UseNoNeedReissue) {
225 return RET_DO;
226 } else if (result == PluginResult::Error) {
227 MMI_HILOGE("pluginIt err name:%{public}s", (*pluginIt)->name_.c_str());
228 }
229 }
230 return RET_NOTDO;
231 }
232
HandleEvent(std::shared_ptr<KeyEvent> keyEvent,InputPluginStage stage)233 int32_t InputPluginManager::HandleEvent(std::shared_ptr<KeyEvent> keyEvent, InputPluginStage stage)
234 {
235 return DoHandleEvent(keyEvent, nullptr, stage);
236 }
237
DoHandleEvent(std::shared_ptr<KeyEvent> keyEvent,InputPlugin * iplugin,InputPluginStage stage)238 int32_t InputPluginManager::DoHandleEvent(
239 std::shared_ptr<KeyEvent> keyEvent, InputPlugin *iplugin, InputPluginStage stage)
240 {
241 if (keyEvent == nullptr) {
242 return RET_NOTDO;
243 }
244 auto it = plugins_.find(stage);
245 if (it == plugins_.end()) {
246 return RET_NOTDO;
247 }
248 CALL_DEBUG_ENTER;
249 auto &plugins = it->second;
250 auto start_plugin = plugins.begin();
251 if (iplugin != nullptr) {
252 auto cur_plugin = std::find_if(plugins.begin(), plugins.end(),
253 [iplugin](const std::shared_ptr<InputPlugin> &plugin) { return plugin.get() == iplugin; });
254 if (cur_plugin == plugins.end()) {
255 return RET_NOTDO;
256 }
257 start_plugin = std::next(cur_plugin);
258 }
259 int64_t beginTime = 0;
260 PluginResult result;
261 int64_t endTime = 0;
262 int64_t lostTime = 0;
263 for (auto pluginIt = start_plugin; pluginIt != plugins.end(); ++pluginIt) {
264 if ((*pluginIt) == nullptr) {
265 continue;
266 }
267 beginTime = GetSysClockTime();
268 result = (*pluginIt)->HandleEvent(keyEvent, stage);
269 endTime = GetSysClockTime();
270 lostTime = endTime - beginTime;
271 if (lostTime >= TIMEOUT_US) {
272 MMI_HILOGE("pluginIt timeout name:%{public}s ,endTime:%{public}" PRId64 ",lostTime:%{public}" PRId64,
273 (*pluginIt)->name_.c_str(), endTime, lostTime);
274 }
275 if (result == PluginResult::UseNeedReissue) {
276 return RET_DO;
277 } else if (result == PluginResult::UseNoNeedReissue) {
278 return RET_DO;
279 } else if (result == PluginResult::Error) {
280 MMI_HILOGE("pluginIt err name:%{public}s", (*pluginIt)->name_.c_str());
281 }
282 }
283 return RET_NOTDO;
284 }
285
286 // LIBINPUT_EVENT_TABLET_TOOL_BUTTON、LIBINPUT_EVENT_TABLET_PAD_BUTTON、LIBINPUT_EVENT_TABLET_PAD_KEY
287 // These few existence termination events are currently not used and will be supplemented after use
IntermediateEndEvent(libinput_event * event)288 bool InputPluginManager::IntermediateEndEvent(libinput_event *event)
289 {
290 const libinput_event_type type = libinput_event_get_type(event);
291 switch (type) {
292 case LIBINPUT_EVENT_POINTER_MOTION:
293 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
294 case LIBINPUT_EVENT_POINTER_AXIS:
295 case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
296 case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
297 case LIBINPUT_EVENT_POINTER_MOTION_TOUCHPAD:
298 case LIBINPUT_EVENT_POINTER_SCROLL_FINGER_END:
299 case LIBINPUT_EVENT_JOYSTICK_AXIS:
300 case LIBINPUT_EVENT_TOUCH_UP:
301 case LIBINPUT_EVENT_TOUCH_MOTION:
302 case LIBINPUT_EVENT_TOUCH_CANCEL:
303 case LIBINPUT_EVENT_TOUCHPAD_UP:
304 case LIBINPUT_EVENT_TOUCHPAD_MOTION:
305 case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
306 case LIBINPUT_EVENT_TABLET_PAD_RING:
307 case LIBINPUT_EVENT_TABLET_PAD_STRIP:
308 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
309 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
310 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
311 case LIBINPUT_EVENT_GESTURE_PINCH_END:
312 case LIBINPUT_EVENT_GESTURE_HOLD_END:
313 return true;
314 case LIBINPUT_EVENT_KEYBOARD_KEY: {
315 struct libinput_event_keyboard *keyboardEvent = libinput_event_get_keyboard_event(event);
316 CHKPF(keyboardEvent);
317 return libinput_event_keyboard_get_key_state(keyboardEvent) == LIBINPUT_KEY_STATE_RELEASED;
318 }
319 case LIBINPUT_EVENT_POINTER_BUTTON:
320 case LIBINPUT_EVENT_POINTER_TAP:
321 case LIBINPUT_EVENT_POINTER_BUTTON_TOUCHPAD: {
322 auto touchpadButtonEvent = libinput_event_get_pointer_event(event);
323 CHKPF(touchpadButtonEvent);
324 return libinput_event_pointer_get_button_state(touchpadButtonEvent) == LIBINPUT_BUTTON_STATE_RELEASED;
325 }
326 case LIBINPUT_EVENT_JOYSTICK_BUTTON: {
327 auto rawBtnEvent = libinput_event_get_joystick_button_event(event);
328 CHKPF(rawBtnEvent);
329 return libinput_event_joystick_button_get_key_state(rawBtnEvent) == LIBINPUT_BUTTON_STATE_RELEASED;
330 }
331 case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: {
332 auto tabletEvent = libinput_event_get_tablet_tool_event(event);
333 CHKPF(tabletEvent);
334 return libinput_event_tablet_tool_get_proximity_state(tabletEvent) ==
335 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT;
336 }
337 case LIBINPUT_EVENT_TABLET_TOOL_TIP: {
338 auto tabletEvent = libinput_event_get_tablet_tool_event(event);
339 CHKPF(tabletEvent);
340 return libinput_event_tablet_tool_get_tip_state(tabletEvent) == LIBINPUT_TABLET_TOOL_TIP_UP;
341 }
342 default:
343 break;
344 }
345 return false;
346 }
347
Init(std::shared_ptr<IInputPlugin> pin)348 int32_t InputPlugin::Init(std::shared_ptr<IInputPlugin> pin)
349 {
350 name_ = pin->GetName();
351 prio_ = pin->GetPriority();
352 stage_ = pin->GetStage();
353 plugin_ = pin;
354 return RET_OK;
355 }
356
UnInit()357 void InputPlugin::UnInit()
358 {
359 CHKPV(plugin_);
360 MMI_HILOGI("InputPlugin UnInit Start name:%{public}s.", name_.c_str());
361 if (unintPlugin_) {
362 unintPlugin_(plugin_);
363 }
364 }
365
DispatchEvent(libinput_event * event,int64_t frameTime)366 void InputPlugin::DispatchEvent(libinput_event *event, int64_t frameTime)
367 {
368 int32_t result = InputPluginManager::GetInstance()->DoHandleEvent(event, frameTime, this, stage_);
369 if (result == RET_NOTDO) {
370 CHKPV(callback_);
371 callback_(event, frameTime);
372 }
373 }
374
DispatchEvent(std::shared_ptr<KeyEvent> keyEvent,InputDispatchStage stage)375 void InputPlugin::DispatchEvent(std::shared_ptr<KeyEvent> keyEvent, InputDispatchStage stage)
376 {
377 int32_t result = InputPluginManager::GetInstance()->DoHandleEvent(keyEvent, this, stage_);
378 if (result == RET_NOTDO) {
379 CHKPV(keyEventCallback_);
380 keyEventCallback_(keyEvent);
381 }
382 }
383
HandleEvent(libinput_event * event,int64_t frameTime)384 PluginResult InputPlugin::HandleEvent(libinput_event *event, int64_t frameTime)
385 {
386 CHKPR(plugin_, PluginResult::NotUse);
387 return plugin_->HandleEvent(event, frameTime);
388 }
389
HandleEvent(std::shared_ptr<KeyEvent> keyEvent,InputPluginStage stage)390 PluginResult InputPlugin::HandleEvent(std::shared_ptr<KeyEvent> keyEvent, InputPluginStage stage)
391 {
392 CHKPR(plugin_, PluginResult::NotUse);
393 return plugin_->HandleEvent(keyEvent, stage);
394 }
395
AddTimer(std::function<void ()> func,int32_t intervalMs,int32_t repeatCount)396 int32_t InputPlugin::AddTimer(std::function<void()> func, int32_t intervalMs, int32_t repeatCount)
397 {
398 if (timerCnt_ >= MAX_TIMER) {
399 return RET_ERR;
400 }
401 int32_t timerId = TimerMgr->AddTimerInternal(intervalMs, repeatCount, func, name_);
402 if (timerId != -1) {
403 timerCnt_++;
404 }
405 return timerId;
406 }
407
RemoveTimer(int32_t id)408 int32_t InputPlugin::RemoveTimer(int32_t id)
409 {
410 int32_t result = TimerMgr->RemoveTimer(id, name_);
411 if (timerCnt_ > 0) {
412 timerCnt_--;
413 }
414 return result;
415 }
416
~InputPlugin()417 InputPlugin::~InputPlugin()
418 {
419 if (handle_) {
420 dlclose(handle_);
421 handle_ = nullptr;
422 }
423 MMI_HILOGI("~InputPlugin");
424 }
425 } // namespace MMI
426 } // namespace OHOS
427