1 /*
2 * Copyright (c) 2024 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 "key_gesture_manager.h"
17
18 #include "account_manager.h"
19 #include "app_state_observer.h"
20 #include "display_event_monitor.h"
21 #include "event_log_helper.h"
22 #include "key_auto_repeat.h"
23 #ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
24 #include "key_monitor_manager.h"
25 #endif // OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
26 #include "timer_manager.h"
27
28 #undef MMI_LOG_DOMAIN
29 #define MMI_LOG_DOMAIN MMI_LOG_HANDLER
30 #undef MMI_LOG_TAG
31 #define MMI_LOG_TAG "KeyGestureManager"
32
33 namespace OHOS {
34 namespace MMI {
35 namespace {
36 constexpr int32_t COMBINATION_KEY_TIMEOUT { 150 };
37 constexpr int32_t INVALID_ENTITY_ID { -1 };
38 constexpr int32_t REPEAT_ONCE { 1 };
39 constexpr size_t MAX_N_PRINTABLE_ITEMS { 5 };
40 constexpr size_t SINGLE_KEY_PRESSED { 1 };
41 }
42
~Handler()43 KeyGestureManager::Handler::~Handler()
44 {
45 ResetTimer();
46 }
47
ResetTimer()48 void KeyGestureManager::Handler::ResetTimer()
49 {
50 if (timerId_ >= 0) {
51 TimerMgr->RemoveTimer(timerId_);
52 timerId_ = INVALID_ENTITY_ID;
53 }
54 }
55
Trigger(std::shared_ptr<KeyEvent> keyEvent)56 void KeyGestureManager::Handler::Trigger(std::shared_ptr<KeyEvent> keyEvent)
57 {
58 MMI_HILOGI("[Handler] Handler(%{public}d) will run after %{public}dms", GetId(), GetLongPressTime());
59 keyEvent_ = KeyEvent::Clone(keyEvent);
60 timerId_ = TimerMgr->AddTimer(GetLongPressTime(), REPEAT_ONCE,
61 [this]() {
62 CHKPV(keyEvent_);
63 Run(keyEvent_);
64 keyEvent_ = nullptr;
65 timerId_ = INVALID_ENTITY_ID;
66 }, "KeyGestureManager");
67 if (timerId_ < 0) {
68 MMI_HILOGI("[Handler] AddTimer fail");
69 }
70 }
71
Run(std::shared_ptr<KeyEvent> keyEvent) const72 void KeyGestureManager::Handler::Run(std::shared_ptr<KeyEvent> keyEvent) const
73 {
74 if (callback_ != nullptr) {
75 MMI_HILOGI("[Handler] Run handler(%{public}d)", GetId());
76 callback_(keyEvent);
77 }
78 }
79
RunPending()80 void KeyGestureManager::Handler::RunPending()
81 {
82 if (keyEvent_ != nullptr) {
83 Run(keyEvent_);
84 keyEvent_ = nullptr;
85 }
86 }
87
IsWorking()88 bool KeyGestureManager::KeyGesture::IsWorking()
89 {
90 return true;
91 }
92
AddHandler(int32_t pid,int32_t longPressTime,std::function<void (std::shared_ptr<KeyEvent>)> callback)93 int32_t KeyGestureManager::KeyGesture::AddHandler(int32_t pid, int32_t longPressTime,
94 std::function<void(std::shared_ptr<KeyEvent>)> callback)
95 {
96 static int32_t baseId { 0 };
97
98 longPressTime = std::max(longPressTime, COMBINATION_KEY_TIMEOUT);
99 return handlers_.emplace_back(++baseId, pid, longPressTime, callback).GetId();
100 }
101
RemoveHandler(int32_t id)102 bool KeyGestureManager::KeyGesture::RemoveHandler(int32_t id)
103 {
104 for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter) {
105 if (iter->GetId() == id) {
106 iter->ResetTimer();
107 handlers_.erase(iter);
108 MMI_HILOGD("Handler(%{public}d) of key gesture was removed", id);
109 return true;
110 }
111 }
112 return false;
113 }
114
Reset()115 void KeyGestureManager::KeyGesture::Reset()
116 {
117 MarkActive(false);
118 ResetTimers();
119 }
120
ResetTimers()121 void KeyGestureManager::KeyGesture::ResetTimers()
122 {
123 for (auto &handler : handlers_) {
124 handler.ResetTimer();
125 }
126 }
127
GetForegroundPids() const128 std::set<int32_t> KeyGestureManager::KeyGesture::GetForegroundPids() const
129 {
130 std::set<int32_t> pids;
131 std::vector<AppExecFwk::AppStateData> appStates = APP_OBSERVER_MGR->GetForegroundAppData();
132 std::for_each(appStates.cbegin(), appStates.cend(), [&pids](auto &appState) {
133 pids.insert(appState.pid);
134 });
135
136 std::ostringstream sPids;
137 size_t nItems = 0;
138
139 if (auto iter = pids.cbegin(); iter != pids.cend()) {
140 sPids << *iter;
141 ++nItems;
142
143 for (++iter; iter != pids.cend(); ++iter) {
144 if (nItems > MAX_N_PRINTABLE_ITEMS) {
145 sPids << ",...";
146 break;
147 }
148 sPids << "," << *iter;
149 ++nItems;
150 }
151 }
152 MMI_HILOGI("Foreground pids: {%{public}zu}[%{public}s]", pids.size(), sPids.str().c_str());
153 return pids;
154 }
155
HaveForegroundHandler(const std::set<int32_t> & foregroundApps) const156 bool KeyGestureManager::KeyGesture::HaveForegroundHandler(const std::set<int32_t> &foregroundApps) const
157 {
158 return std::any_of(handlers_.cbegin(), handlers_.cend(), [&foregroundApps](const auto &handler) {
159 return (foregroundApps.find(handler.GetPid()) != foregroundApps.cend());
160 });
161 }
162
TriggerHandlers(std::shared_ptr<KeyEvent> keyEvent)163 void KeyGestureManager::KeyGesture::TriggerHandlers(std::shared_ptr<KeyEvent> keyEvent)
164 {
165 std::set<int32_t> foregroundPids = GetForegroundPids();
166 bool haveForeground = HaveForegroundHandler(foregroundPids);
167 ShowHandlers(std::string("TriggerHandlers"), foregroundPids);
168
169 for (auto &handler : handlers_) {
170 if (!haveForeground || (foregroundPids.find(handler.GetPid()) != foregroundPids.end())) {
171 handler.Trigger(keyEvent);
172 }
173 }
174 }
175
RunHandler(int32_t handlerId,std::shared_ptr<KeyEvent> keyEvent)176 void KeyGestureManager::KeyGesture::RunHandler(int32_t handlerId, std::shared_ptr<KeyEvent> keyEvent)
177 {
178 for (auto &handler : handlers_) {
179 if (handler.GetId() == handlerId) {
180 handler.ResetTimer();
181 handler.Run(keyEvent);
182 break;
183 }
184 }
185 }
186
NotifyHandlers(std::shared_ptr<KeyEvent> keyEvent)187 void KeyGestureManager::KeyGesture::NotifyHandlers(std::shared_ptr<KeyEvent> keyEvent)
188 {
189 std::set<int32_t> foregroundPids = GetForegroundPids();
190 bool haveForeground = HaveForegroundHandler(foregroundPids);
191 ShowHandlers(std::string("NotifyHandlers"), foregroundPids);
192
193 for (auto &handler : handlers_) {
194 if (!haveForeground || (foregroundPids.find(handler.GetPid()) != foregroundPids.end())) {
195 handler.Run(keyEvent);
196 }
197 }
198 }
199
ShowHandlers(const std::string & prefix,const std::set<int32_t> & foregroundPids) const200 void KeyGestureManager::KeyGesture::ShowHandlers(
201 const std::string &prefix, const std::set<int32_t> &foregroundPids) const
202 {
203 std::ostringstream output;
204 size_t nHandlers = 0;
205
206 for (const auto &handler : handlers_) {
207 if (nHandlers > MAX_N_PRINTABLE_ITEMS) {
208 output << "...";
209 break;
210 }
211 ++nHandlers;
212 output << "[" << handler.GetId() << "," << handler.GetPid()
213 << (foregroundPids.find(handler.GetPid()) != foregroundPids.cend() ? ",F]" : ",B]");
214 }
215 MMI_HILOGI("[KeyGesture] %{public}s {%{public}zu}%{public}s",
216 prefix.c_str(), handlers_.size(), output.str().c_str());
217 }
218
ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const219 bool KeyGestureManager::LongPressSingleKey::ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const
220 {
221 std::set<int32_t> keys = keyOption->GetPreKeys();
222 return (keys.empty() &&
223 (keyOption->GetFinalKey() == keyCode_) &&
224 keyOption->IsFinalKeyDown() &&
225 (keyOption->GetFinalKeyDownDuration() < COMBINATION_KEY_TIMEOUT));
226 }
227
Intercept(std::shared_ptr<KeyEvent> keyEvent)228 bool KeyGestureManager::LongPressSingleKey::Intercept(std::shared_ptr<KeyEvent> keyEvent)
229 {
230 if ((keyEvent->GetKeyCode() == keyCode_) && (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_DOWN)) {
231 if (IsActive()) {
232 int64_t now = GetSysClockTime();
233 if ((now >= (firstDownTime_ + MS2US(COMBINATION_KEY_TIMEOUT))) &&
234 !KeyMonitorIntercept(keyEvent)) {
235 NotifyHandlers(keyEvent);
236 }
237 } else {
238 firstDownTime_ = GetSysClockTime();
239 MarkActive(true);
240 if (!KeyMonitorIntercept(keyEvent, COMBINATION_KEY_TIMEOUT)) {
241 TriggerHandlers(keyEvent);
242 }
243 }
244 return true;
245 }
246 if (IsActive()) {
247 #ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
248 KEY_MONITOR_MGR->NotifyPendingMonitors();
249 #endif // OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
250 Reset();
251 RunPendingHandlers();
252 }
253 return false;
254 }
255
Dump(std::ostringstream & output) const256 void KeyGestureManager::LongPressSingleKey::Dump(std::ostringstream &output) const
257 {
258 output << "[*] --> {";
259 if (auto iter = handlers_.begin(); iter != handlers_.end()) {
260 output << iter->GetLongPressTime();
261 for (++iter; iter != handlers_.end(); ++iter) {
262 output << "," << iter->GetLongPressTime();
263 }
264 }
265 output << "}";
266 }
267
Reset()268 void KeyGestureManager::LongPressSingleKey::Reset()
269 {
270 #ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
271 KEY_MONITOR_MGR->ResetAll(keyCode_);
272 #endif // OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
273 KeyGesture::Reset();
274 }
275
RunPendingHandlers()276 void KeyGestureManager::LongPressSingleKey::RunPendingHandlers()
277 {
278 std::set<int32_t> foregroundPids = GetForegroundPids();
279 bool haveForeground = HaveForegroundHandler(foregroundPids);
280 ShowHandlers(std::string("RunPendingHandlers"), foregroundPids);
281
282 for (auto &handler : handlers_) {
283 if (!haveForeground || (foregroundPids.find(handler.GetPid()) != foregroundPids.end())) {
284 handler.RunPending();
285 }
286 }
287 }
288
ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const289 bool KeyGestureManager::LongPressCombinationKey::ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const
290 {
291 std::set<int32_t> keys = keyOption->GetPreKeys();
292 keys.insert(keyOption->GetFinalKey());
293 return (keys_ == keys);
294 }
295
Intercept(std::shared_ptr<KeyEvent> keyEvent)296 bool KeyGestureManager::LongPressCombinationKey::Intercept(std::shared_ptr<KeyEvent> keyEvent)
297 {
298 if ((keys_.find(keyEvent->GetKeyCode()) != keys_.end()) &&
299 (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_DOWN)) {
300 if (IsActive()) {
301 std::ostringstream output;
302 Dump(output);
303 if (EventLogHelper::IsBetaVersion() && !keyEvent->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
304 MMI_HILOGI("[LongPressCombinationKey] %s is active now", output.str().c_str());
305 } else {
306 MMI_HILOGI("[LongPressCombinationKey] %s is active now", output.str().c_str());
307 }
308 return true;
309 }
310 if (!IsWorking()) {
311 std::ostringstream output;
312 Dump(output);
313 if (EventLogHelper::IsBetaVersion() && !keyEvent->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
314 MMI_HILOGI("[LongPressCombinationKey] Switch off %s", output.str().c_str());
315 } else {
316 MMI_HILOGI("[LongPressCombinationKey] Switch off %s", output.str().c_str());
317 }
318 return false;
319 }
320 if (handlers_.empty()) {
321 std::ostringstream output;
322 Dump(output);
323 if (EventLogHelper::IsBetaVersion() && !keyEvent->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
324 MMI_HILOGI("[LongPressCombinationKey] No handler for %s", output.str().c_str());
325 } else {
326 MMI_HILOGI("[LongPressCombinationKey] No handler for %s", output.str().c_str());
327 }
328 return false;
329 }
330 if (RecognizeGesture(keyEvent)) {
331 TriggerAll(keyEvent);
332 return true;
333 }
334 }
335 if (IsActive()) {
336 Reset();
337 }
338 return UpdateConsumed(keyEvent);
339 }
340
Dump(std::ostringstream & output) const341 void KeyGestureManager::LongPressCombinationKey::Dump(std::ostringstream &output) const
342 {
343 output << "[**] --> {";
344 if (auto iter = handlers_.begin(); iter != handlers_.end()) {
345 output << "(ID:" << iter->GetId() << ",T:" << iter->GetLongPressTime() << ")";
346 for (++iter; iter != handlers_.end(); ++iter) {
347 output << ",(ID:" << iter->GetId() << ",T:" << iter->GetLongPressTime() << ")";
348 }
349 }
350 output << "}";
351 }
352
MarkKeyConsumed()353 void KeyGestureManager::LongPressCombinationKey::MarkKeyConsumed()
354 {
355 consumedKeys_.insert(keys_.cbegin(), keys_.cend());
356 }
357
UpdateConsumed(std::shared_ptr<KeyEvent> keyEvent)358 bool KeyGestureManager::LongPressCombinationKey::UpdateConsumed(std::shared_ptr<KeyEvent> keyEvent)
359 {
360 if (((keyEvent->GetKeyAction() != KeyEvent::KEY_ACTION_UP) &&
361 (keyEvent->GetKeyAction() != KeyEvent::KEY_ACTION_CANCEL)) ||
362 (keys_.find(keyEvent->GetKeyCode()) == keys_.cend())) {
363 return false;
364 }
365 if (auto iter = consumedKeys_.find(keyEvent->GetKeyCode()); iter != consumedKeys_.cend()) {
366 consumedKeys_.erase(iter);
367 return true;
368 }
369 return false;
370 }
371
RecognizeGesture(std::shared_ptr<KeyEvent> keyEvent)372 bool KeyGestureManager::LongPressCombinationKey::RecognizeGesture(std::shared_ptr<KeyEvent> keyEvent)
373 {
374 if ((keyEvent->GetPressedKeys().size() == SINGLE_KEY_PRESSED) && !keyEvent->IsRepeatKey()) {
375 firstDownTime_ = GetSysClockTime();
376 }
377 int64_t now = GetSysClockTime();
378 return std::all_of(keys_.cbegin(), keys_.cend(), [this, keyEvent, now](auto keyCode) {
379 auto itemOpt = keyEvent->GetKeyItem(keyCode);
380 return (itemOpt && itemOpt->IsPressed() &&
381 (now < (firstDownTime_ + MS2US(COMBINATION_KEY_TIMEOUT))));
382 });
383 }
384
TriggerAll(std::shared_ptr<KeyEvent> keyEvent)385 void KeyGestureManager::LongPressCombinationKey::TriggerAll(std::shared_ptr<KeyEvent> keyEvent)
386 {
387 MarkActive(true);
388 std::ostringstream output;
389 Dump(output);
390 if (EventLogHelper::IsBetaVersion() && !keyEvent->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
391 MMI_HILOGI("[LongPressCombinationKey] trigger %s", output.str().c_str());
392 } else {
393 MMI_HILOGI("[LongPressCombinationKey] trigger %s", output.str().c_str());
394 }
395 OnTriggerAll(keyEvent);
396 TriggerHandlers(keyEvent);
397 }
398
PullUpAccessibility()399 KeyGestureManager::PullUpAccessibility::PullUpAccessibility()
400 : LongPressCombinationKey(std::set({ KeyEvent::KEYCODE_VOLUME_DOWN, KeyEvent::KEYCODE_VOLUME_UP }))
401 {}
402
IsWorking()403 bool KeyGestureManager::PullUpAccessibility::IsWorking()
404 {
405 if ((DISPLAY_MONITOR->GetScreenStatus() == EventFwk::CommonEventSupport::COMMON_EVENT_SCREEN_OFF)) {
406 return false;
407 }
408 if (DISPLAY_MONITOR->GetScreenLocked()) {
409 return ACCOUNT_MGR->GetCurrentAccountSetting().GetAccShortcutEnabledOnScreenLocked();
410 } else {
411 return ACCOUNT_MGR->GetCurrentAccountSetting().GetAccShortcutEnabled();
412 }
413 }
414
AddHandler(int32_t pid,int32_t longPressTime,std::function<void (std::shared_ptr<KeyEvent>)> callback)415 int32_t KeyGestureManager::PullUpAccessibility::AddHandler(int32_t pid,
416 int32_t longPressTime, std::function<void(std::shared_ptr<KeyEvent>)> callback)
417 {
418 return KeyGesture::AddHandler(pid, ACCOUNT_MGR->GetCurrentAccountSetting().GetAccShortcutTimeout(),
419 [this, callback](std::shared_ptr<KeyEvent> keyEvent) {
420 MarkKeyConsumed();
421 if (callback != nullptr) {
422 callback(keyEvent);
423 }
424 });
425 }
426
OnTriggerAll(std::shared_ptr<KeyEvent> keyEvent)427 void KeyGestureManager::PullUpAccessibility::OnTriggerAll(std::shared_ptr<KeyEvent> keyEvent)
428 {
429 MMI_HILOGI("[PullUpAccessibility] Current AccShortcutTimeout setting:%{public}dms",
430 ACCOUNT_MGR->GetCurrentAccountSetting().GetAccShortcutTimeout());
431 for (auto &handler : handlers_) {
432 handler.SetLongPressTime(ACCOUNT_MGR->GetCurrentAccountSetting().GetAccShortcutTimeout());
433 }
434 }
435
KeyGestureManager()436 KeyGestureManager::KeyGestureManager()
437 {
438 keyGestures_.push_back(std::make_unique<PullUpAccessibility>());
439 keyGestures_.push_back(std::make_unique<LongPressSingleKey>(KeyEvent::KEYCODE_VOLUME_DOWN));
440 keyGestures_.push_back(std::make_unique<LongPressSingleKey>(KeyEvent::KEYCODE_VOLUME_UP));
441 }
442
ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const443 bool KeyGestureManager::ShouldIntercept(std::shared_ptr<KeyOption> keyOption) const
444 {
445 CALL_INFO_TRACE;
446 CHKPF(keyOption);
447 return std::any_of(keyGestures_.cbegin(), keyGestures_.cend(),
448 [keyOption](const auto &keyGesture) {
449 return keyGesture->ShouldIntercept(keyOption);
450 });
451 }
452
AddKeyGesture(int32_t pid,std::shared_ptr<KeyOption> keyOption,std::function<void (std::shared_ptr<KeyEvent>)> callback)453 int32_t KeyGestureManager::AddKeyGesture(int32_t pid, std::shared_ptr<KeyOption> keyOption,
454 std::function<void(std::shared_ptr<KeyEvent>)> callback)
455 {
456 CHKPR(keyOption, INVALID_ENTITY_ID);
457 for (auto &keyGesture : keyGestures_) {
458 CHKPC(keyGesture);
459 if (keyGesture->ShouldIntercept(keyOption)) {
460 auto downDuration = std::max(keyOption->GetFinalKeyDownDuration(), COMBINATION_KEY_TIMEOUT);
461 return keyGesture->AddHandler(pid, downDuration, callback);
462 }
463 }
464 return INVALID_ENTITY_ID;
465 }
466
RemoveKeyGesture(int32_t id)467 void KeyGestureManager::RemoveKeyGesture(int32_t id)
468 {
469 for (auto &keyGesture : keyGestures_) {
470 if (keyGesture->RemoveHandler(id)) {
471 break;
472 }
473 }
474 }
475
Intercept(std::shared_ptr<KeyEvent> keyEvent)476 bool KeyGestureManager::Intercept(std::shared_ptr<KeyEvent> keyEvent)
477 {
478 CALL_INFO_TRACE;
479 CHKPF(keyEvent);
480 for (auto iter = keyGestures_.begin(); iter != keyGestures_.end(); ++iter) {
481 CHKPC(*iter);
482 if ((*iter)->Intercept(keyEvent)) {
483 std::ostringstream output;
484 (*iter)->Dump(output);
485 if (EventLogHelper::IsBetaVersion() && !keyEvent->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
486 MMI_HILOGI("Intercepted by %s", output.str().c_str());
487 } else {
488 MMI_HILOGI("Intercepted by %s", output.str().c_str());
489 }
490 for (++iter; iter != keyGestures_.end(); ++iter) {
491 (*iter)->Reset();
492 }
493 return true;
494 }
495 }
496 return false;
497 }
498
ResetAll()499 void KeyGestureManager::ResetAll()
500 {
501 for (auto &keyGesture : keyGestures_) {
502 CHKPC(keyGesture);
503 keyGesture->Reset();
504 }
505 }
506
Dump() const507 void KeyGestureManager::Dump() const
508 {
509 for (const auto &keyGesture : keyGestures_) {
510 std::ostringstream output;
511 CHKPC(keyGesture);
512 keyGesture->Dump(output);
513 MMI_HILOGI("%s", output.str().c_str());
514 }
515 }
516
KeyMonitorIntercept(std::shared_ptr<KeyEvent> keyEvent)517 bool KeyGestureManager::KeyMonitorIntercept(std::shared_ptr<KeyEvent> keyEvent)
518 {
519 #ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
520 return KEY_MONITOR_MGR->Intercept(keyEvent);
521 #else
522 return false;
523 #endif // OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
524 }
525
KeyMonitorIntercept(std::shared_ptr<KeyEvent> keyEvent,int32_t delay)526 bool KeyGestureManager::KeyMonitorIntercept(std::shared_ptr<KeyEvent> keyEvent, int32_t delay)
527 {
528 #ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
529 return KEY_MONITOR_MGR->Intercept(keyEvent, delay);
530 #else
531 return false;
532 #endif // OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
533 }
534 } // namespace MMI
535 } // namespace OHOS
536