1/*
2 * Copyright (C) 2022 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 "accessibility_touchEvent_injector.h"
17#include "accessible_ability_manager_service.h"
18#include "hilog_wrapper.h"
19#include "utils.h"
20#include <cinttypes>
21
22namespace OHOS {
23namespace Accessibility {
24namespace {
25    constexpr int32_t MS_TO_US = 1000;
26    constexpr int32_t MOVE_GESTURE_MIN_PATH_COUNT = 2;
27} // namespace
28
29TouchInjectHandler::TouchInjectHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner,
30    TouchEventInjector &server) : AppExecFwk::EventHandler(runner), server_(server)
31{
32}
33
34void TouchInjectHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
35{
36    std::shared_ptr<SendEventArgs> parameters = nullptr;
37    if (!event) {
38        HILOG_ERROR("event is nullptr");
39        return;
40    }
41    switch (event->GetInnerEventId()) {
42        case TouchEventInjector::SEND_TOUCH_EVENT_MSG:
43            parameters = event->GetSharedObject<SendEventArgs>();
44            if (parameters == nullptr) {
45                HILOG_ERROR("parameters is nullptr");
46                return;
47            }
48            if (!parameters->event_) {
49                HILOG_WARN("pointer event is nullptr");
50                return;
51            }
52            server_.SendPointerEvent(*parameters->event_);
53            break;
54        default:
55            break;
56    }
57}
58
59TouchEventInjector::TouchEventInjector()
60{
61    runner_ = Singleton<AccessibleAbilityManagerService>::GetInstance().GetMainRunner();
62    if (!runner_) {
63        HILOG_ERROR("get runner failed");
64        return;
65    }
66    handler_ = std::make_shared<TouchInjectHandler>(runner_, *this);
67    if (!handler_) {
68        HILOG_ERROR("create event handler failed");
69        return;
70    }
71}
72
73bool TouchEventInjector::OnPointerEvent(MMI::PointerEvent &event)
74{
75    HILOG_DEBUG();
76
77    EventTransmission::OnPointerEvent(event);
78    return false;
79}
80
81void TouchEventInjector::DestroyEvents()
82{
83    HILOG_DEBUG();
84    CancelInjectedEvents();
85    isDestroyEvent_ = true;
86    EventTransmission::DestroyEvents();
87}
88
89void TouchEventInjector::SendPointerEvent(MMI::PointerEvent &event)
90{
91    HILOG_DEBUG();
92    if (GetNext() != nullptr) {
93        EventTransmission::OnPointerEvent(event);
94        if (event.GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_DOWN) {
95            isGestureUnderway_ = true;
96        }
97        if (event.GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_UP) {
98            isGestureUnderway_ = false;
99        }
100    }
101}
102
103void TouchEventInjector::CancelGesture()
104{
105    HILOG_DEBUG();
106    std::shared_ptr<MMI::PointerEvent> event;
107    MMI::PointerEvent::PointerItem pointer = {};
108    int64_t time = GetSystemTime();
109    pointer.SetDownTime(time);
110    pointer.SetPointerId(0);
111    if (GetNext() != nullptr && isGestureUnderway_) {
112        event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_CANCEL, pointer, time);
113        if (event == nullptr) {
114            HILOG_ERROR("event is nullptr");
115            return;
116        }
117
118        SendPointerEvent(*event);
119        isGestureUnderway_ = false;
120    }
121}
122
123void TouchEventInjector::CancelInjectedEvents()
124{
125    HILOG_DEBUG();
126    if (handler_ == nullptr) {
127        HILOG_ERROR("handler_ is nullptr");
128        return;
129    }
130    if (handler_->HasInnerEvent(SEND_TOUCH_EVENT_MSG)) {
131        handler_->RemoveEvent(SEND_TOUCH_EVENT_MSG);
132        CancelGesture();
133    }
134}
135
136std::shared_ptr<MMI::PointerEvent> TouchEventInjector::obtainTouchEvent(int32_t action,
137    MMI::PointerEvent::PointerItem point, int64_t actionTime)
138{
139    HILOG_DEBUG();
140    std::shared_ptr<MMI::PointerEvent> pointerEvent = MMI::PointerEvent::Create();
141    if (pointerEvent == nullptr) {
142        HILOG_ERROR("pointerEvent is nullptr");
143        return nullptr;
144    }
145
146    pointerEvent->SetPointerId(point.GetPointerId());
147    pointerEvent->SetTargetDisplayId(0);
148    pointerEvent->SetPointerAction(action);
149    pointerEvent->SetActionTime(actionTime);
150    pointerEvent->SetActionStartTime(point.GetDownTime());
151    pointerEvent->AddPointerItem(point);
152    pointerEvent->SetSourceType(MMI::PointerEvent::SOURCE_TYPE_TOUCHSCREEN);
153    return pointerEvent;
154}
155
156int64_t TouchEventInjector::GetSystemTime()
157{
158    HILOG_DEBUG();
159
160    int64_t microsecond = Utils::GetSystemTime() * 1000;
161    return microsecond;
162}
163
164void TouchEventInjector::InjectEvents(const std::shared_ptr<AccessibilityGestureInjectPath>& gesturePath)
165{
166    HILOG_DEBUG();
167
168    int64_t curTime = GetSystemTime();
169    if (isDestroyEvent_ || !GetNext()) {
170        HILOG_WARN("Inject gesture fail");
171        return;
172    }
173    CancelInjectedEvents();
174    CancelGesture();
175
176    ParseTouchEventsFromGesturePath(curTime, gesturePath);
177
178    if (injectedEvents_.empty()) {
179        HILOG_WARN("No injected events");
180        return;
181    }
182    for (size_t i = 0; i < injectedEvents_.size(); i++) {
183        std::shared_ptr<SendEventArgs> parameters = std::make_shared<SendEventArgs>();
184        parameters->event_ = injectedEvents_[i];
185        if (injectedEvents_[i]) {
186            int64_t timeout = (injectedEvents_[i]->GetActionTime() - curTime) / MS_TO_US;
187            if (timeout < 0) {
188                HILOG_WARN("timeout is error.%{public}" PRId64 "", timeout);
189            } else {
190                handler_->SendEvent(SEND_TOUCH_EVENT_MSG, parameters, timeout);
191            }
192        }
193    }
194    injectedEvents_.clear();
195}
196
197void TouchEventInjector::ParseTapsEvents(int64_t startTime,
198    const std::shared_ptr<AccessibilityGestureInjectPath>& gesturePath)
199{
200    HILOG_DEBUG();
201
202    if (!gesturePath) {
203        HILOG_ERROR("gesturePath is null.");
204        return;
205    }
206    const std::vector<AccessibilityGesturePosition> &positions = gesturePath->GetPositions();
207    size_t positionSize = positions.size();
208    if (!positionSize) {
209        HILOG_WARN("PositionSize is zero.");
210        return;
211    }
212    int64_t durationTime = gesturePath->GetDurationTime();
213    if (durationTime < 0) {
214        HILOG_WARN("DurationTime is wrong.");
215        return;
216    }
217    int64_t perDurationTime = static_cast<int64_t>(static_cast<uint64_t>(durationTime) / positionSize);
218    int64_t downTime = startTime;
219    for (size_t i = 0; i < positionSize; i++) {
220        std::shared_ptr<MMI::PointerEvent> event;
221        MMI::PointerEvent::PointerItem pointer = {};
222        pointer.SetPointerId(0);
223        // Append down event
224        int32_t px = static_cast<int32_t>(positions[i].positionX_);
225        int32_t py = static_cast<int32_t>(positions[i].positionY_);
226        pointer.SetDisplayX(px);
227        pointer.SetDisplayY(py);
228        pointer.SetDownTime(downTime);
229        event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_DOWN, pointer, downTime);
230        if (event == nullptr) {
231            HILOG_ERROR("event is nullptr");
232            return;
233        }
234
235        HILOG_DEBUG("append down event");
236        injectedEvents_.push_back(event);
237
238        // Append up event
239        int64_t upTime = downTime + perDurationTime * MS_TO_US;
240        event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_UP, pointer, upTime);
241        if (event == nullptr) {
242            HILOG_ERROR("event is nullptr");
243            return;
244        }
245
246        HILOG_DEBUG("append up event");
247        injectedEvents_.push_back(event);
248        downTime = upTime + DOUBLE_TAP_MIN_TIME;
249    }
250}
251
252void TouchEventInjector::ParseMovesEvents(int64_t startTime,
253    const std::shared_ptr<AccessibilityGestureInjectPath>& gesturePath)
254{
255    HILOG_DEBUG();
256
257    if (!gesturePath) {
258        HILOG_ERROR("gesturePath is null.");
259        return;
260    }
261    std::vector<AccessibilityGesturePosition> positions = gesturePath->GetPositions();
262    size_t positionSize = positions.size();
263    if (positionSize < MOVE_GESTURE_MIN_PATH_COUNT) {
264        HILOG_WARN("PositionSize is wrong.");
265        return;
266    }
267    int64_t durationTime = gesturePath->GetDurationTime();
268    if (durationTime < 0) {
269        HILOG_WARN("DurationTime is wrong.");
270        return;
271    }
272    int64_t perDurationTime = static_cast<int64_t>(static_cast<uint64_t>(durationTime) / (positionSize - 1));
273    int64_t downTime = startTime;
274    int64_t nowTime = startTime;
275    for (size_t i = 0; i < positionSize; i++) {
276        std::shared_ptr<MMI::PointerEvent> event;
277        MMI::PointerEvent::PointerItem pointer = {};
278        int32_t px = static_cast<int32_t>(positions[i].positionX_);
279        int32_t py = static_cast<int32_t>(positions[i].positionY_);
280        pointer.SetPointerId(0);
281        pointer.SetDisplayX(px);
282        pointer.SetDisplayY(py);
283        pointer.SetDownTime(downTime);
284        if (i == 0) { // Append down event
285            HILOG_DEBUG("append down event");
286            event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_DOWN, pointer, downTime);
287            if (event == nullptr) {
288                HILOG_ERROR("event is nullptr");
289                return;
290            }
291
292            injectedEvents_.push_back(event);
293        } else if (i < (positionSize - 1)) { // Append move event
294            HILOG_DEBUG("append move event");
295            nowTime += perDurationTime * MS_TO_US;
296            event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_MOVE, pointer, nowTime);
297            if (event == nullptr) {
298                HILOG_ERROR("event is nullptr");
299                return;
300            }
301
302            injectedEvents_.push_back(event);
303        } else { // Append up event
304            HILOG_DEBUG("append move event");
305            nowTime += perDurationTime * MS_TO_US;
306            event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_MOVE, pointer, nowTime);
307            if (event == nullptr) {
308                HILOG_ERROR("event is nullptr");
309                return;
310            }
311
312            injectedEvents_.push_back(event);
313
314            HILOG_DEBUG("append up event");
315            event = obtainTouchEvent(MMI::PointerEvent::POINTER_ACTION_UP, pointer, nowTime);
316            if (event == nullptr) {
317                HILOG_ERROR("event is nullptr");
318                return;
319            }
320
321            injectedEvents_.push_back(event);
322        }
323    }
324}
325
326void TouchEventInjector::ParseTouchEventsFromGesturePath(int64_t startTime,
327    const std::shared_ptr<AccessibilityGestureInjectPath>& gesturePath)
328{
329    HILOG_DEBUG();
330    if (!gesturePath) {
331        HILOG_ERROR("gesturePath is null.");
332        return;
333    }
334    const std::vector<AccessibilityGesturePosition> &positions = gesturePath->GetPositions();
335    if (positions.size() == 0) {
336        HILOG_ERROR("position size is 0.");
337        return;
338    }
339    if ((positions.size() == 1) ||
340        ((positions[0].positionX_ == positions[1].positionX_) &&
341        (positions[0].positionY_ == positions[1].positionY_))) {
342        ParseTapsEvents(startTime, gesturePath);
343    } else {
344        ParseMovesEvents(startTime, gesturePath);
345    }
346}
347} // namespace Accessibility
348} // namespace OHOS