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 #include "intention_event_manager.h"
16 
17 #ifdef IMF_ENABLE
18 #include <input_method_controller.h>
19 #endif // IMF_ENABLE
20 #include "scene_session.h"
21 #include "session_helper.h"
22 #include "session_manager/include/scene_session_manager.h"
23 #include "window_manager_hilog.h"
24 #include <hitrace_meter.h>
25 #include "parameters.h"
26 #include "xcollie/xcollie.h"
27 
28 namespace OHOS {
29 namespace Rosen {
30 namespace {
31 constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, HILOG_DOMAIN_WINDOW, "IntentionEventManager" };
32 constexpr int32_t TRANSPARENT_FINGER_ID = 10000;
33 std::shared_ptr<MMI::PointerEvent> g_lastMouseEvent = nullptr;
34 int32_t g_lastLeaveWindowId = -1;
35 constexpr int32_t DELAY_TIME = 15;
36 constexpr unsigned int FREQUENT_CLICK_TIME_LIMIT = 3;
37 constexpr int FREQUENT_CLICK_COUNT_LIMIT = 8;
38 static const bool IS_BETA = OHOS::system::GetParameter("const.logsystem.versiontype", "").find("beta") !=
39     std::string::npos;
40 
LogPointInfo(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)41 void LogPointInfo(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
42 {
43     if (pointerEvent == nullptr) {
44         return;
45     }
46 
47     uint32_t windowId = static_cast<uint32_t>(pointerEvent->GetTargetWindowId());
48     TLOGD(WmsLogTag::WMS_EVENT, "point source:%{public}d", pointerEvent->GetSourceType());
49     auto actionId = pointerEvent->GetPointerId();
50     int32_t action = pointerEvent->GetPointerAction();
51     if (action == MMI::PointerEvent::POINTER_ACTION_MOVE) {
52         return;
53     }
54 
55     MMI::PointerEvent::PointerItem item;
56     if (pointerEvent->GetPointerItem(actionId, item)) {
57         TLOGD(WmsLogTag::WMS_EVENT, "action point info:windowid:%{public}d,id:%{public}d,displayx:%{private}d,"
58             "displayy:%{private}d,windowx:%{private}d,windowy:%{private}d,action:%{public}d pressure:"
59             "%{public}f,tiltx:%{public}f,tiltY:%{public}f",
60             windowId, actionId, item.GetDisplayX(), item.GetDisplayY(), item.GetWindowX(), item.GetWindowY(),
61             pointerEvent->GetPointerAction(), item.GetPressure(), item.GetTiltX(), item.GetTiltY());
62     }
63     auto ids = pointerEvent->GetPointerIds();
64     for (auto&& id :ids) {
65         MMI::PointerEvent::PointerItem item;
66         if (pointerEvent->GetPointerItem(id, item)) {
67             TLOGD(WmsLogTag::WMS_EVENT, "all point info: id:%{public}d,x:%{private}d,y:%{private}d,"
68                 "isPressend:%{public}d,pressure:%{public}f,tiltX:%{public}f,tiltY:%{public}f",
69             actionId, item.GetWindowX(), item.GetWindowY(), item.IsPressed(), item.GetPressure(),
70             item.GetTiltX(), item.GetTiltY());
71         }
72     }
73 }
74 } // namespace
75 
IntentionEventManager()76 IntentionEventManager::IntentionEventManager() {}
~IntentionEventManager()77 IntentionEventManager::~IntentionEventManager() {}
78 
~InputEventListener()79 IntentionEventManager::InputEventListener::~InputEventListener()
80 {
81     std::lock_guard<std::mutex> guard(mouseEventMutex_);
82     g_lastMouseEvent = nullptr;
83 }
84 
EnableInputEventListener(Ace::UIContent* uiContent, std::shared_ptr<AppExecFwk::EventHandler> eventHandler)85 bool IntentionEventManager::EnableInputEventListener(Ace::UIContent* uiContent,
86     std::shared_ptr<AppExecFwk::EventHandler> eventHandler)
87 {
88     if (uiContent == nullptr) {
89         TLOGE(WmsLogTag::WMS_EVENT, "EnableInputEventListener uiContent is null");
90         return false;
91     }
92     if (eventHandler == nullptr) {
93         TLOGE(WmsLogTag::WMS_EVENT, "EnableInputEventListener eventHandler is null");
94         return false;
95     }
96     auto listener =
97         std::make_shared<IntentionEventManager::InputEventListener>(uiContent, eventHandler);
98     MMI::InputManager::GetInstance()->SetWindowInputEventConsumer(listener, eventHandler);
99     TLOGI(WmsLogTag::WMS_EVENT, "SetWindowInputEventConsumer success");
100     if (IS_BETA) {
101         // Xcollie's SetTimerCounter task is set with the params to record count and time of the input down event
102         int id = HiviewDFX::XCollie::GetInstance().SetTimerCount("FREQUENT_CLICK_WARNING", FREQUENT_CLICK_TIME_LIMIT,
103             FREQUENT_CLICK_COUNT_LIMIT);
104     }
105     return true;
106 }
107 
UpdateLastMouseEvent( std::shared_ptr<MMI::PointerEvent> pointerEvent) const108 void IntentionEventManager::InputEventListener::UpdateLastMouseEvent(
109     std::shared_ptr<MMI::PointerEvent> pointerEvent) const
110 {
111     if (pointerEvent == nullptr) {
112         TLOGE(WmsLogTag::WMS_EVENT, "UpdateLastMouseEvent pointerEvent is null");
113         return;
114     }
115     g_lastLeaveWindowId = -1;
116     if ((pointerEvent->GetSourceType() == MMI::PointerEvent::SOURCE_TYPE_MOUSE) &&
117         (pointerEvent->GetPointerAction() != MMI::PointerEvent::POINTER_ACTION_LEAVE_WINDOW)) {
118         std::lock_guard<std::mutex> guard(mouseEventMutex_);
119         g_lastMouseEvent = std::make_shared<MMI::PointerEvent>(*pointerEvent);
120     } else if (g_lastMouseEvent != nullptr) {
121         TLOGD(WmsLogTag::WMS_EVENT, "Clear last mouse event");
122         std::lock_guard<std::mutex> guard(mouseEventMutex_);
123         g_lastMouseEvent = nullptr;
124         SceneSession::ClearEnterWindow();
125     }
126 }
127 
CheckPointerEvent( const std::shared_ptr<MMI::PointerEvent> pointerEvent) const128 bool IntentionEventManager::InputEventListener::CheckPointerEvent(
129     const std::shared_ptr<MMI::PointerEvent> pointerEvent) const
130 {
131     if (pointerEvent == nullptr) {
132         TLOGE(WmsLogTag::WMS_EVENT, "pointerEvent is null");
133         return false;
134     }
135     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "IEM:PointerEvent id:%d action:%d",
136         pointerEvent->GetId(), pointerEvent->GetPointerAction());
137     if (uiContent_ == nullptr) {
138         TLOGE(WmsLogTag::WMS_EVENT, "uiContent_ is null");
139         pointerEvent->MarkProcessed();
140         return false;
141     }
142     if (!SceneSessionManager::GetInstance().IsInputEventEnabled()) {
143         TLOGW(WmsLogTag::WMS_EVENT, "inputEvent is disabled temporarily, eventId is %{public}d", pointerEvent->GetId());
144         pointerEvent->MarkProcessed();
145         return false;
146     }
147     return true;
148 }
149 
SetPointerId(std::shared_ptr<MMI::PointerEvent>& pointerEvent)150 void SetPointerId(std::shared_ptr<MMI::PointerEvent>& pointerEvent)
151 {
152     auto dispatchTimes = pointerEvent->GetDispatchTimes();
153     if (dispatchTimes > 0) {
154         MMI::PointerEvent::PointerItem pointerItem;
155         auto pointerId = pointerEvent->GetPointerId();
156         if (pointerEvent->GetPointerItem(pointerId, pointerItem)) {
157             pointerItem.SetPointerId(pointerId + dispatchTimes * TRANSPARENT_FINGER_ID);
158             pointerEvent->UpdatePointerItem(pointerId, pointerItem);
159             pointerEvent->SetPointerId(pointerId + dispatchTimes * TRANSPARENT_FINGER_ID);
160         }
161     }
162 }
163 
OnInputEvent( std::shared_ptr<MMI::PointerEvent> pointerEvent) const164 void IntentionEventManager::InputEventListener::OnInputEvent(
165     std::shared_ptr<MMI::PointerEvent> pointerEvent) const
166 {
167     if (!CheckPointerEvent(pointerEvent)) {
168         return;
169     }
170     LogPointInfo(pointerEvent);
171     int32_t action = pointerEvent->GetPointerAction();
172     uint32_t windowId = static_cast<uint32_t>(pointerEvent->GetTargetWindowId());
173     auto sceneSession = SceneSessionManager::GetInstance().GetSceneSession(windowId);
174     if (sceneSession == nullptr) {
175         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "The scene session is nullptr");
176         pointerEvent->MarkProcessed();
177         return;
178     }
179     SetPointerId(pointerEvent);
180     if (action != MMI::PointerEvent::POINTER_ACTION_MOVE) {
181         static uint32_t eventId = 0;
182         TLOGI(WmsLogTag::WMS_INPUT_KEY_FLOW, "eid:%{public}d,InputId:%{public}d,wid:%{public}u"
183             ",windowName:%{public}s,action:%{public}d,isSystem:%{public}d", eventId++, pointerEvent->GetId(), windowId,
184             sceneSession->GetSessionInfo().abilityName_.c_str(), action, sceneSession->GetSessionInfo().isSystem_);
185     }
186     if (sceneSession->GetSessionInfo().isSystem_) {
187         sceneSession->SendPointerEventToUI(pointerEvent);
188         // notify touchOutside and touchDown event
189         if (action == MMI::PointerEvent::POINTER_ACTION_DOWN ||
190             action == MMI::PointerEvent::POINTER_ACTION_BUTTON_DOWN) {
191             MMI::PointerEvent::PointerItem pointerItem;
192             if (pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), pointerItem)) {
193                 sceneSession->ProcessPointDownSession(pointerItem.GetDisplayX(), pointerItem.GetDisplayY());
194             }
195             if (IS_BETA) {
196                 /*
197                 * Triggers input down event recorded.
198                 * If the special num of the events reached within the sepcial time interval,
199                 * a panic behavior is reported.
200                 */
201                 HiviewDFX::XCollie::GetInstance().TriggerTimerCount("FREQUENT_CLICK_WARNING", true, "");
202             }
203         }
204     } else {
205         // transfer pointer event for move and drag
206         WSError ret = sceneSession->TransferPointerEvent(pointerEvent);
207         if (sceneSession->GetWindowType() == WindowType::WINDOW_TYPE_SYSTEM_FLOAT) {
208             sceneSession->NotifyOutsideDownEvent(pointerEvent);
209         }
210         if ((ret != WSError::WS_OK || static_cast<int32_t>(getprocpid()) != sceneSession->GetCallingPid()) &&
211             pointerEvent != nullptr) {
212             pointerEvent->MarkProcessed();
213         }
214     }
215     UpdateLastMouseEvent(pointerEvent);
216 }
217 
DispatchKeyEventCallback( int32_t focusedSessionId, std::shared_ptr<MMI::KeyEvent> keyEvent, bool consumed) const218 void IntentionEventManager::InputEventListener::DispatchKeyEventCallback(
219     int32_t focusedSessionId, std::shared_ptr<MMI::KeyEvent> keyEvent, bool consumed) const
220 {
221     if (keyEvent == nullptr) {
222         WLOGFW("keyEvent is null, focusedSessionId:%{public}" PRId32
223             ", consumed:%{public}" PRId32, focusedSessionId, consumed);
224         return;
225     }
226 
227     if (consumed) {
228         WLOGD("Input method has processed key event, id:%{public}" PRId32 ", focusedSessionId:%{public}" PRId32,
229             keyEvent->GetId(), focusedSessionId);
230         return;
231     }
232 
233     auto focusedSceneSession = SceneSessionManager::GetInstance().GetSceneSession(focusedSessionId);
234     if (focusedSceneSession == nullptr) {
235         WLOGFE("focusedSceneSession is null");
236         keyEvent->MarkProcessed();
237         return;
238     }
239 
240     if (uiContent_ == nullptr) {
241         WLOGFE("uiContent_ is null");
242         keyEvent->MarkProcessed();
243         return;
244     }
245 
246     focusedSceneSession->SendKeyEventToUI(keyEvent);
247 }
248 
OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const249 void IntentionEventManager::InputEventListener::OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const
250 {
251     if (keyEvent == nullptr) {
252         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "The key event is nullptr");
253         return;
254     }
255     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "IEM:KeyEvent id:%d", keyEvent->GetId());
256     if (!SceneSessionManager::GetInstance().IsInputEventEnabled()) {
257         TLOGD(WmsLogTag::WMS_INPUT_KEY_FLOW, "OnInputEvent is disabled temporarily");
258         keyEvent->MarkProcessed();
259         return;
260     }
261     auto focusedSessionId = SceneSessionManager::GetInstance().GetFocusedSessionId();
262     if (focusedSessionId == INVALID_SESSION_ID) {
263         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "focusedSessionId is invalid");
264         keyEvent->MarkProcessed();
265         return;
266     }
267     auto focusedSceneSession = SceneSessionManager::GetInstance().GetSceneSession(focusedSessionId);
268     if (focusedSceneSession == nullptr) {
269         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "focusedSceneSession is null");
270         keyEvent->MarkProcessed();
271         return;
272     }
273     auto isSystem = focusedSceneSession->GetSessionInfo().isSystem_;
274     static uint32_t eventId = 0;
275     TLOGI(WmsLogTag::WMS_INPUT_KEY_FLOW, "eid:%{public}d,InputId:%{public}d,wid:%{public}u"
276         ",focusId:%{public}d,isSystem:%{public}d",
277         eventId++, keyEvent->GetId(), keyEvent->GetTargetWindowId(), focusedSessionId, isSystem);
278     if (!isSystem) {
279         WSError ret = focusedSceneSession->TransferKeyEvent(keyEvent);
280         if ((ret != WSError::WS_OK || static_cast<int32_t>(getprocpid()) != focusedSceneSession->GetCallingPid()) &&
281             keyEvent != nullptr) {
282             keyEvent->MarkProcessed();
283         }
284         return;
285     }
286     bool isConsumed = focusedSceneSession->SendKeyEventToUI(keyEvent, true);
287     if (isConsumed) {
288         TLOGI(WmsLogTag::WMS_INPUT_KEY_FLOW, "SendKeyEventToUI id:%{public}d isConsumed:%{public}d",
289             keyEvent->GetId(), static_cast<int>(isConsumed));
290         return;
291     }
292 #ifdef IMF_ENABLE
293     bool isKeyboardEvent = IsKeyboardEvent(keyEvent);
294     if (isKeyboardEvent) {
295         WLOGD("Async dispatch keyEvent to input method");
296         auto callback = [this, focusedSessionId] (std::shared_ptr<MMI::KeyEvent>& keyEvent, bool consumed) {
297             this->DispatchKeyEventCallback(focusedSessionId, keyEvent, consumed);
298         };
299         auto ret = MiscServices::InputMethodController::GetInstance()->DispatchKeyEvent(keyEvent, callback);
300         if (ret != 0) {
301             WLOGFE("DispatchKeyEvent failed, ret:%{public}d, id:%{public}d, focusedSessionId:%{public}d",
302                 ret, keyEvent->GetId(), focusedSessionId);
303             DispatchKeyEventCallback(focusedSessionId, keyEvent, false);
304         }
305         return;
306     }
307 #endif // IMF_ENABLE
308     TLOGD(WmsLogTag::WMS_INPUT_KEY_FLOW, "Syetem window scene, transfer key event to root scene");
309     if (uiContent_ == nullptr) {
310         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "uiContent_ is null");
311         keyEvent->MarkProcessed();
312         return;
313     }
314     focusedSceneSession->SendKeyEventToUI(keyEvent);
315 }
316 
IsKeyboardEvent( const std::shared_ptr<MMI::KeyEvent>& keyEvent) const317 bool IntentionEventManager::InputEventListener::IsKeyboardEvent(
318     const std::shared_ptr<MMI::KeyEvent>& keyEvent) const
319 {
320     int32_t keyCode = keyEvent->GetKeyCode();
321     bool isKeyFN = (keyCode == MMI::KeyEvent::KEYCODE_FN);
322     bool isKeyBack = (keyCode == MMI::KeyEvent::KEYCODE_BACK);
323     bool isKeyboard = (keyCode >= MMI::KeyEvent::KEYCODE_0 && keyCode <= MMI::KeyEvent::KEYCODE_NUMPAD_RIGHT_PAREN);
324     bool isKeySound = (keyCode == MMI::KeyEvent::KEYCODE_SOUND);
325     TLOGD(WmsLogTag::WMS_EVENT, "isKeyFN:%{public}d, isKeyboard:%{public}d", isKeyFN, isKeyboard);
326     return (isKeyFN || isKeyboard || isKeyBack || isKeySound);
327 }
328 
OnInputEvent( std::shared_ptr<MMI::AxisEvent> axisEvent) const329 void IntentionEventManager::InputEventListener::OnInputEvent(
330     std::shared_ptr<MMI::AxisEvent> axisEvent) const
331 {
332     if (axisEvent == nullptr) {
333         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "axisEvent is nullptr");
334         return;
335     }
336     if (uiContent_ == nullptr) {
337         TLOGE(WmsLogTag::WMS_INPUT_KEY_FLOW, "uiContent_ is null");
338         axisEvent->MarkProcessed();
339         return;
340     }
341     if (!(uiContent_->ProcessAxisEvent(axisEvent))) {
342         TLOGI(WmsLogTag::WMS_INPUT_KEY_FLOW, "The UI content consumes the axis event failed.");
343         axisEvent->MarkProcessed();
344     }
345 }
346 }
347 } // namespace OHOS::Rosen
348