1 /*
2 * Copyright (c) 2021-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 "mouse_event_normalize.h"
17
18 #include <cinttypes>
19
20 #include "input-event-codes.h"
21
22 #include "define_multimodal.h"
23 #include "event_log_helper.h"
24 #include "i_pointer_drawing_manager.h"
25 #include "input_device_manager.h"
26 #include "input_event_handler.h"
27 #include "input_windows_manager.h"
28 #include "mouse_device_state.h"
29 #include "timer_manager.h"
30 #include "util_ex.h"
31 #include "util.h"
32
33 namespace OHOS {
34 namespace MMI {
35 namespace {
36 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MMI_LOG_DOMAIN, "MouseEventNormalize" };
37 const std::vector<AccelerateCurve> ACCELERATE_CURVES {
38 { { 8, 32, 128 }, { 0.16, 0.30, 0.56 }, { 0.0, -1.12, -9.44 } },
39 { { 8, 32, 128 }, { 0.32, 0.60, 1.12 }, { 0.0, -2.24, -18.88 } },
40 { { 8, 32, 128 }, { 0.48, 0.90, 1.68 }, { 0.0, -3.36, -28.32 } },
41 { { 8, 32, 128 }, { 0.64, 1.20, 2.24 }, { 0.0, -4.48, -37.76 } },
42 { { 8, 32, 128 }, { 0.80, 1.50, 2.80 }, { 0.0, -5.60, -47.20 } },
43 { { 8, 32, 128 }, { 0.86, 1.95, 3.64 }, { 0.0, -8.72, -62.80 } },
44 { { 8, 32, 128 }, { 0.92, 2.40, 4.48 }, { 0.0, -11.84, -78.40 } },
45 { { 8, 32, 128 }, { 0.98, 2.85, 5.32 }, { 0.0, -14.96, -94.00 } },
46 { { 8, 32, 128 }, { 1.04, 3.30, 6.16 }, { 0.0, -18.08, -109.60 } },
47 { { 8, 32, 128 }, { 1.10, 3.75, 7.00 }, { 0.0, -21.20, -125.20 } },
48 { { 8, 32, 128 }, { 1.16, 4.20, 7.84 }, { 0.0, -24.32, -140.80 } }
49 };
50 constexpr double DOUBLE_ZERO = 1e-6;
51 constexpr int32_t MIN_SPEED = 1;
52 constexpr int32_t MAX_SPEED = 11;
53 #ifdef OHOS_BUILD_ENABLE_COOPERATE
54 constexpr double PERCENT_CONST = 100.0;
55 #endif // OHOS_BUILD_ENABLE_COOPERATE
56 } // namespace
MouseEventNormalize()57 MouseEventNormalize::MouseEventNormalize()
58 {
59 pointerEvent_ = PointerEvent::Create();
60 CHKPL(pointerEvent_);
61 }
62
~MouseEventNormalize()63 MouseEventNormalize::~MouseEventNormalize() {}
64
GetPointerEvent() const65 std::shared_ptr<PointerEvent> MouseEventNormalize::GetPointerEvent() const
66 {
67 return pointerEvent_;
68 }
69
GetSpeedGain(double vin,double & gain) const70 bool MouseEventNormalize::GetSpeedGain(double vin, double &gain) const
71 {
72 if (fabs(vin) < DOUBLE_ZERO) {
73 MMI_HILOGE("The value of the parameter passed in is 0");
74 return false;
75 }
76 if (speed_ < 1) {
77 MMI_HILOGE("The speed value can't be less than 1");
78 return false;
79 }
80 const AccelerateCurve& curve = ACCELERATE_CURVES[speed_ - 1];
81 int32_t num = static_cast<int32_t>(ceil(fabs(vin)));
82 for (size_t i = 0; i < curve.speeds.size(); ++i) {
83 if (num <= curve.speeds[i]) {
84 gain = (curve.slopes[i] * vin + curve.diffNums[i]) / vin;
85 return true;
86 }
87 }
88 gain = (curve.slopes.back() * vin + curve.diffNums.back()) / vin;
89 return true;
90 }
91
HandleMotionInner(struct libinput_event_pointer * data)92 int32_t MouseEventNormalize::HandleMotionInner(struct libinput_event_pointer* data)
93 {
94 CALL_DEBUG_ENTER;
95 CHKPR(data, ERROR_NULL_POINTER);
96 CHKPR(pointerEvent_, ERROR_NULL_POINTER);
97 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_MOVE);
98 pointerEvent_->SetButtonId(buttonId_);
99
100 InitAbsolution();
101 if (currentDisplayId_ == -1) {
102 absolutionX_ = -1;
103 absolutionY_ = -1;
104 MMI_HILOGI("The currentDisplayId_ is -1");
105 return RET_ERR;
106 }
107
108 int32_t ret = HandleMotionAccelerate(data);
109 if (ret != RET_OK) {
110 MMI_HILOGE("Failed to handle motion correction");
111 return ret;
112 }
113 WinMgr->UpdateAndAdjustMouseLocation(currentDisplayId_, absolutionX_, absolutionY_);
114 pointerEvent_->SetTargetDisplayId(currentDisplayId_);
115 MMI_HILOGD("Change coordinate: x:%{public}lf, y:%{public}lf, currentDisplayId_:%{public}d",
116 absolutionX_, absolutionY_, currentDisplayId_);
117 return RET_OK;
118 }
119
HandleMotionAccelerate(struct libinput_event_pointer * data)120 int32_t MouseEventNormalize::HandleMotionAccelerate(struct libinput_event_pointer* data)
121 {
122 CHKPR(data, ERROR_NULL_POINTER);
123 double dx = libinput_event_pointer_get_dx(data);
124 double dy = libinput_event_pointer_get_dy(data);
125 double vin = (fmax(abs(dx), abs(dy)) + fmin(abs(dx), abs(dy))) / 2.0;
126 double gain { 0.0 };
127 if (!GetSpeedGain(vin, gain)) {
128 MMI_HILOGE("Get speed gain failed");
129 return RET_ERR;
130 }
131 double correctionX = dx * gain;
132 double correctionY = dy * gain;
133 MMI_HILOGD("Get and process the movement coordinates, dx:%{public}lf, dy:%{public}lf,"
134 "correctionX:%{public}lf, correctionY:%{public}lf, gain:%{public}lf",
135 dx, dy, correctionX, correctionY, gain);
136 absolutionX_ += correctionX;
137 absolutionY_ += correctionY;
138 return RET_OK;
139 }
140
InitAbsolution()141 void MouseEventNormalize::InitAbsolution()
142 {
143 if (absolutionX_ != -1 || absolutionY_ != -1 || currentDisplayId_ != -1) {
144 return;
145 }
146 MMI_HILOGD("Init absolution");
147 auto dispalyGroupInfo = WinMgr->GetDisplayGroupInfo();
148 if (dispalyGroupInfo.displaysInfo.empty()) {
149 MMI_HILOGI("The displayInfo is empty");
150 return;
151 }
152 currentDisplayId_ = dispalyGroupInfo.displaysInfo[0].id;
153 absolutionX_ = dispalyGroupInfo.displaysInfo[0].width * 1.0 / 2;
154 absolutionY_ = dispalyGroupInfo.displaysInfo[0].height * 1.0 / 2;
155 }
156
HandleButtonInner(struct libinput_event_pointer * data)157 int32_t MouseEventNormalize::HandleButtonInner(struct libinput_event_pointer* data)
158 {
159 CALL_DEBUG_ENTER;
160 CHKPR(data, ERROR_NULL_POINTER);
161 MMI_HILOGD("Current action:%{public}d", pointerEvent_->GetPointerAction());
162
163 auto ret = HandleButtonValueInner(data);
164 if (ret != RET_OK) {
165 MMI_HILOGE("The button value does not exist");
166 return RET_ERR;
167 }
168 uint32_t button = libinput_event_pointer_get_button(data);
169 auto state = libinput_event_pointer_get_button_state(data);
170 if (state == LIBINPUT_BUTTON_STATE_RELEASED) {
171 MouseState->MouseBtnStateCounts(button, BUTTON_STATE_RELEASED);
172 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_UP);
173 int32_t buttonId = MouseState->LibinputChangeToPointer(button);
174 pointerEvent_->DeleteReleaseButton(buttonId);
175 isPressed_ = false;
176 buttonId_ = PointerEvent::BUTTON_NONE;
177 } else if (state == LIBINPUT_BUTTON_STATE_PRESSED) {
178 MouseState->MouseBtnStateCounts(button, BUTTON_STATE_PRESSED);
179 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_DOWN);
180 int32_t buttonId = MouseState->LibinputChangeToPointer(button);
181 pointerEvent_->SetButtonPressed(buttonId);
182 isPressed_ = true;
183 buttonId_ = pointerEvent_->GetButtonId();
184 } else {
185 MMI_HILOGE("Unknown state, state:%{public}u", state);
186 return RET_ERR;
187 }
188 return RET_OK;
189 }
190
HandleButtonValueInner(struct libinput_event_pointer * data)191 int32_t MouseEventNormalize::HandleButtonValueInner(struct libinput_event_pointer* data)
192 {
193 CALL_DEBUG_ENTER;
194 CHKPR(data, ERROR_NULL_POINTER);
195
196 uint32_t button = libinput_event_pointer_get_button(data);
197 int32_t buttonId = MouseState->LibinputChangeToPointer(button);
198 if (buttonId == PointerEvent::BUTTON_NONE) {
199 MMI_HILOGE("Unknown btn, btn:%{public}u", button);
200 return RET_ERR;
201 }
202 pointerEvent_->SetButtonId(buttonId);
203 return RET_OK;
204 }
205
HandleAxisInner(struct libinput_event_pointer * data)206 int32_t MouseEventNormalize::HandleAxisInner(struct libinput_event_pointer* data)
207 {
208 CALL_DEBUG_ENTER;
209 CHKPR(data, ERROR_NULL_POINTER);
210 if (buttonId_ == PointerEvent::BUTTON_NONE && pointerEvent_->GetButtonId() != PointerEvent::BUTTON_NONE) {
211 pointerEvent_->SetButtonId(PointerEvent::BUTTON_NONE);
212 }
213 if (TimerMgr->IsExist(timerId_)) {
214 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_AXIS_UPDATE);
215 TimerMgr->ResetTimer(timerId_);
216 MMI_HILOGD("Axis update");
217 } else {
218 static constexpr int32_t timeout = 100;
219 std::weak_ptr<MouseEventNormalize> weakPtr = shared_from_this();
220 timerId_ = TimerMgr->AddTimer(timeout, 1, [weakPtr]() {
221 CALL_DEBUG_ENTER;
222 auto sharedPtr = weakPtr.lock();
223 CHKPV(sharedPtr);
224 MMI_HILOGD("timer:%{public}d", sharedPtr->timerId_);
225 sharedPtr->timerId_ = -1;
226 auto pointerEvent = sharedPtr->GetPointerEvent();
227 CHKPV(pointerEvent);
228 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_AXIS_END);
229 pointerEvent->UpdateId();
230 auto inputEventNormalizeHandler = InputHandler->GetEventNormalizeHandler();
231 CHKPV(inputEventNormalizeHandler);
232 inputEventNormalizeHandler->HandlePointerEvent(pointerEvent);
233 });
234
235 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_AXIS_BEGIN);
236 MMI_HILOGD("Axis begin");
237 }
238
239 if (libinput_event_pointer_has_axis(data, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
240 double axisValue = libinput_event_pointer_get_axis_value(data, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
241 pointerEvent_->SetAxisValue(PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, axisValue);
242 }
243 if (libinput_event_pointer_has_axis(data, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
244 double axisValue = libinput_event_pointer_get_axis_value(data, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
245 pointerEvent_->SetAxisValue(PointerEvent::AXIS_TYPE_SCROLL_HORIZONTAL, axisValue);
246 }
247 return RET_OK;
248 }
249
HandlePostInner(struct libinput_event_pointer * data,int32_t deviceId,PointerEvent::PointerItem & pointerItem)250 void MouseEventNormalize::HandlePostInner(struct libinput_event_pointer* data, int32_t deviceId,
251 PointerEvent::PointerItem &pointerItem)
252 {
253 CALL_DEBUG_ENTER;
254 CHKPV(data);
255 auto mouseInfo = WinMgr->GetMouseInfo();
256 MouseState->SetMouseCoords(mouseInfo.physicalX, mouseInfo.physicalY);
257 pointerItem.SetDisplayX(mouseInfo.physicalX);
258 pointerItem.SetDisplayY(mouseInfo.physicalY);
259 pointerItem.SetWindowX(0);
260 pointerItem.SetWindowY(0);
261 pointerItem.SetPointerId(0);
262 pointerItem.SetPressed(isPressed_);
263
264 int64_t time = GetSysClockTime();
265 pointerItem.SetDownTime(time);
266 pointerItem.SetWidth(0);
267 pointerItem.SetHeight(0);
268 pointerItem.SetPressure(0);
269 pointerItem.SetToolType(PointerEvent::TOOL_TYPE_FINGER);
270 pointerItem.SetDeviceId(deviceId);
271 #ifdef OHOS_BUILD_ENABLE_COOPERATE
272 SetDxDyForDInput(pointerItem, data);
273 #endif // OHOS_BUILD_ENABLE_COOPERATE
274 pointerEvent_->UpdateId();
275 pointerEvent_->UpdatePointerItem(pointerEvent_->GetPointerId(), pointerItem);
276 pointerEvent_->SetSourceType(PointerEvent::SOURCE_TYPE_MOUSE);
277 pointerEvent_->SetActionTime(time);
278 pointerEvent_->SetActionStartTime(time);
279 pointerEvent_->SetDeviceId(deviceId);
280 pointerEvent_->SetPointerId(0);
281 pointerEvent_->SetTargetDisplayId(currentDisplayId_);
282 pointerEvent_->SetTargetWindowId(-1);
283 pointerEvent_->SetAgentWindowId(-1);
284 }
285
Normalize(struct libinput_event * event)286 int32_t MouseEventNormalize::Normalize(struct libinput_event *event)
287 {
288 CALL_DEBUG_ENTER;
289 CHKPR(event, ERROR_NULL_POINTER);
290 auto data = libinput_event_get_pointer_event(event);
291 CHKPR(data, ERROR_NULL_POINTER);
292 CHKPR(pointerEvent_, ERROR_NULL_POINTER);
293 pointerEvent_->ClearAxisValue();
294 int32_t result;
295 const int32_t type = libinput_event_get_type(event);
296 switch (type) {
297 case LIBINPUT_EVENT_POINTER_MOTION:
298 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
299 result = HandleMotionInner(data);
300 break;
301 }
302 case LIBINPUT_EVENT_POINTER_BUTTON: {
303 result = HandleButtonInner(data);
304 break;
305 }
306 case LIBINPUT_EVENT_POINTER_AXIS: {
307 result = HandleAxisInner(data);
308 break;
309 }
310 default: {
311 MMI_HILOGE("Unknow type:%{public}d", type);
312 return RET_ERR;
313 }
314 }
315 int32_t deviceId = InputDevMgr->FindInputDeviceId(libinput_event_get_device(event));
316 PointerEvent::PointerItem pointerItem;
317 HandlePostInner(data, deviceId, pointerItem);
318 WinMgr->UpdateTargetPointer(pointerEvent_);
319 DumpInner();
320 return result;
321 }
322 #ifdef OHOS_BUILD_ENABLE_POINTER_DRAWING
HandleMotionMoveMouse(int32_t offsetX,int32_t offsetY)323 void MouseEventNormalize::HandleMotionMoveMouse(int32_t offsetX, int32_t offsetY)
324 {
325 CALL_DEBUG_ENTER;
326 CHKPV(pointerEvent_);
327 pointerEvent_->SetPointerAction(PointerEvent::POINTER_ACTION_MOVE);
328 InitAbsolution();
329 absolutionX_ += offsetX;
330 absolutionY_ += offsetY;
331 WinMgr->UpdateAndAdjustMouseLocation(currentDisplayId_, absolutionX_, absolutionY_);
332 }
333
OnDisplayLost(int32_t displayId)334 void MouseEventNormalize::OnDisplayLost(int32_t displayId)
335 {
336 if (currentDisplayId_ != displayId) {
337 currentDisplayId_ = -1;
338 absolutionX_ = -1;
339 absolutionY_ = -1;
340 InitAbsolution();
341 WinMgr->UpdateAndAdjustMouseLocation(currentDisplayId_, absolutionX_, absolutionY_);
342 }
343 }
344
GetDisplayId() const345 int32_t MouseEventNormalize::GetDisplayId() const
346 {
347 return currentDisplayId_;
348 }
349
HandlePostMoveMouse(PointerEvent::PointerItem & pointerItem)350 void MouseEventNormalize::HandlePostMoveMouse(PointerEvent::PointerItem& pointerItem)
351 {
352 CALL_DEBUG_ENTER;
353 auto mouseInfo = WinMgr->GetMouseInfo();
354 CHKPV(pointerEvent_);
355 MouseState->SetMouseCoords(mouseInfo.physicalX, mouseInfo.physicalY);
356 pointerItem.SetDisplayX(mouseInfo.physicalX);
357 pointerItem.SetDisplayY(mouseInfo.physicalY);
358 pointerItem.SetWindowX(0);
359 pointerItem.SetWindowY(0);
360 pointerItem.SetPointerId(0);
361 pointerItem.SetPressed(isPressed_);
362
363 int64_t time = GetSysClockTime();
364 pointerItem.SetDownTime(time);
365 pointerItem.SetWidth(0);
366 pointerItem.SetHeight(0);
367 pointerItem.SetPressure(0);
368
369 pointerEvent_->UpdateId();
370 pointerEvent_->UpdatePointerItem(pointerEvent_->GetPointerId(), pointerItem);
371 pointerEvent_->SetSourceType(PointerEvent::SOURCE_TYPE_MOUSE);
372 pointerEvent_->SetActionTime(time);
373 pointerEvent_->SetActionStartTime(time);
374
375 pointerEvent_->SetPointerId(0);
376 pointerEvent_->SetTargetDisplayId(-1);
377 pointerEvent_->SetTargetWindowId(-1);
378 pointerEvent_->SetAgentWindowId(-1);
379 }
380
NormalizeMoveMouse(int32_t offsetX,int32_t offsetY)381 bool MouseEventNormalize::NormalizeMoveMouse(int32_t offsetX, int32_t offsetY)
382 {
383 CALL_DEBUG_ENTER;
384 CHKPF(pointerEvent_);
385 bool bHasPoinerDevice = InputDevMgr->HasPointerDevice();
386 if (!bHasPoinerDevice) {
387 MMI_HILOGE("There hasn't any pointer device");
388 return false;
389 }
390
391 PointerEvent::PointerItem pointerItem;
392 HandleMotionMoveMouse(offsetX, offsetY);
393 HandlePostMoveMouse(pointerItem);
394 DumpInner();
395 return bHasPoinerDevice;
396 }
397 #endif // OHOS_BUILD_ENABLE_POINTER_DRAWING
398
DumpInner()399 void MouseEventNormalize::DumpInner()
400 {
401 EventLogHelper::PrintEventData(pointerEvent_);
402 }
403
Dump(int32_t fd,const std::vector<std::string> & args)404 void MouseEventNormalize::Dump(int32_t fd, const std::vector<std::string> &args)
405 {
406 CALL_DEBUG_ENTER;
407 PointerEvent::PointerItem item;
408 CHKPV(pointerEvent_);
409 pointerEvent_->GetPointerItem(pointerEvent_->GetPointerId(), item);
410 mprintf(fd, "Mouse device state information:\t");
411 mprintf(fd,
412 "PointerId:%d | SourceType:%s | PointerAction:%s | WindowX:%d | WindowY:%d | ButtonId:%d "
413 "| AgentWindowId:%d | TargetWindowId:%d | DownTime:%" PRId64 " | IsPressed:%s \t",
414 pointerEvent_->GetPointerId(), pointerEvent_->DumpSourceType(), pointerEvent_->DumpPointerAction(),
415 item.GetWindowX(), item.GetWindowY(), pointerEvent_->GetButtonId(), pointerEvent_->GetAgentWindowId(),
416 pointerEvent_->GetTargetWindowId(), item.GetDownTime(), item.IsPressed() ? "true" : "false");
417 }
418
SetPointerSpeed(int32_t speed)419 int32_t MouseEventNormalize::SetPointerSpeed(int32_t speed)
420 {
421 CALL_DEBUG_ENTER;
422 if (speed < MIN_SPEED) {
423 speed_ = MIN_SPEED;
424 } else if (speed > MAX_SPEED) {
425 speed_ = MAX_SPEED;
426 } else {
427 speed_ = speed;
428 }
429 MMI_HILOGD("Set pointer speed:%{public}d", speed_);
430 return RET_OK;
431 }
432
GetPointerSpeed() const433 int32_t MouseEventNormalize::GetPointerSpeed() const
434 {
435 CALL_DEBUG_ENTER;
436 MMI_HILOGD("Get pointer speed:%{public}d", speed_);
437 return speed_;
438 }
439
440 #ifdef OHOS_BUILD_ENABLE_COOPERATE
SetDxDyForDInput(PointerEvent::PointerItem & pointerItem,libinput_event_pointer * data)441 void MouseEventNormalize::SetDxDyForDInput(PointerEvent::PointerItem& pointerItem, libinput_event_pointer* data)
442 {
443 double dx = libinput_event_pointer_get_dx(data);
444 double dy = libinput_event_pointer_get_dy(data);
445 int32_t rawDx = static_cast<int32_t>(dx);
446 int32_t rawDy = static_cast<int32_t>(dy);
447 pointerItem.SetRawDx(rawDx);
448 pointerItem.SetRawDy(rawDy);
449 MMI_HILOGD("MouseEventNormalize SetDxDyForDInput, dx:%{public}d, dy:%{public}d", rawDx, rawDy);
450 }
451
SetAbsolutionLocation(double xPercent,double yPercent)452 void MouseEventNormalize::SetAbsolutionLocation(double xPercent, double yPercent)
453 {
454 MMI_HILOGI("Cross screen location, xPercent:%{public}lf, yPercent:%{public}lf",
455 xPercent, yPercent);
456 auto displayGroupInfo = WinMgr->GetDisplayGroupInfo();
457 if (currentDisplayId_ == -1) {
458 if (displayGroupInfo.displaysInfo.empty()) {
459 MMI_HILOGI("The displayInfo is empty");
460 return;
461 }
462 currentDisplayId_ = displayGroupInfo.displaysInfo[0].id;
463 }
464 struct DisplayInfo display;
465 for (auto &it : displayGroupInfo.displaysInfo) {
466 if (it.id == currentDisplayId_) {
467 display = it;
468 break;
469 }
470 }
471 absolutionX_ = display.width * xPercent / PERCENT_CONST;
472 absolutionY_ = display.height * yPercent / PERCENT_CONST;
473 WinMgr->UpdateAndAdjustMouseLocation(currentDisplayId_, absolutionX_, absolutionY_);
474 int32_t physicalX = WinMgr->GetMouseInfo().physicalX;
475 int32_t physicalY = WinMgr->GetMouseInfo().physicalY;
476 IPointerDrawingManager::GetInstance()->SetPointerLocation(getpid(), physicalX, physicalY);
477 }
478 #endif // OHOS_BUILD_ENABLE_COOPERATE
479 } // namespace MMI
480 } // namespace OHOS
481