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 <atomic> 17 #include <chrono> 18 #include <fstream> 19 #include <memory> 20 #include <iostream> 21 #include <thread> 22 #include <utility> 23 #include <condition_variable> 24 #include <sys/mman.h> 25 #ifdef HIDUMPER_ENABLED 26 #include <iservice_registry.h> 27 #include <system_ability_load_callback_stub.h> 28 #include "idump_broker.h" 29 #include "dump_broker_proxy.h" 30 #include "system_ability_definition.h" 31 #endif 32 #include "pasteboard_client.h" 33 #include "accessibility_event_info.h" 34 #include "accessibility_ui_test_ability.h" 35 #include "ability_manager_client.h" 36 #include "display_manager.h" 37 #include "screen_manager.h" 38 #include "input_manager.h" 39 #include "png.h" 40 #include "wm_common.h" 41 #include "element_node_iterator_impl.h" 42 #include "system_ui_controller.h" 43 #include "test_server_client.h" 44 45 using namespace std; 46 using namespace chrono; 47 48 namespace OHOS::uitest { 49 using namespace std; 50 using namespace nlohmann; 51 using namespace OHOS::MMI; 52 using namespace OHOS::Accessibility; 53 using namespace OHOS::Rosen; 54 using namespace OHOS::Media; 55 using namespace OHOS::HiviewDFX; 56 using namespace OHOS; 57 58 class UiEventMonitor final : public AccessibleAbilityListener { 59 public: 60 virtual ~UiEventMonitor() override = default; 61 62 void OnAbilityConnected() override; 63 64 void OnAbilityDisconnected() override; 65 66 void OnAccessibilityEvent(const AccessibilityEventInfo &eventInfo) override; 67 68 void SetOnAbilityConnectCallback(function<void()> onConnectCb); 69 70 void SetOnAbilityDisConnectCallback(function<void()> onDisConnectCb); 71 OnKeyPressEvent(const shared_ptr<MMI::KeyEvent> & keyEvent)72 bool OnKeyPressEvent(const shared_ptr<MMI::KeyEvent> &keyEvent) override 73 { 74 return false; 75 } 76 77 uint64_t GetLastEventMillis(); 78 79 bool WaitEventIdle(uint32_t idleThresholdMs, uint32_t timeoutMs); 80 81 void WaitScrollCompelete(); 82 83 void RegisterUiEventListener(shared_ptr<UiEventListener> listerner); 84 85 private: 86 function<void()> onConnectCallback_ = nullptr; 87 function<void()> onDisConnectCallback_ = nullptr; 88 atomic<uint64_t> lastEventMillis_ = 0; 89 atomic<uint64_t> lastScrollBeginEventMillis_ = 0; 90 atomic<bool> scrollCompelete_ = true; 91 vector<shared_ptr<UiEventListener>> listeners_; 92 }; 93 94 struct EventSpec { 95 std::string_view componentTyep; 96 int32_t eventType; 97 std::string_view event; 98 }; 99 100 const std::map<char, int32_t> SingleKeySymbalMap = { 101 {' ', OHOS::MMI::KeyEvent::KEYCODE_SPACE}, 102 {'`', OHOS::MMI::KeyEvent::KEYCODE_GRAVE}, 103 {'[', OHOS::MMI::KeyEvent::KEYCODE_LEFT_BRACKET}, 104 {']', OHOS::MMI::KeyEvent::KEYCODE_RIGHT_BRACKET}, 105 {'\\', OHOS::MMI::KeyEvent::KEYCODE_BACKSLASH}, 106 {',', OHOS::MMI::KeyEvent::KEYCODE_COMMA}, 107 {';', OHOS::MMI::KeyEvent::KEYCODE_SEMICOLON}, 108 {'\'', OHOS::MMI::KeyEvent::KEYCODE_APOSTROPHE}, 109 {'/', OHOS::MMI::KeyEvent::KEYCODE_SLASH}, 110 {'*', OHOS::MMI::KeyEvent::KEYCODE_NUMPAD_MULTIPLY}, 111 {'-', OHOS::MMI::KeyEvent::KEYCODE_MINUS}, 112 {'.', OHOS::MMI::KeyEvent::KEYCODE_PERIOD}, 113 {'=', OHOS::MMI::KeyEvent::KEYCODE_EQUALS} 114 }; 115 116 const std::map<char, int32_t> MultiKeySymbalMap = { 117 {'~', OHOS::MMI::KeyEvent::KEYCODE_GRAVE}, 118 {'!', OHOS::MMI::KeyEvent::KEYCODE_1}, 119 {'@', OHOS::MMI::KeyEvent::KEYCODE_2}, 120 {'#', OHOS::MMI::KeyEvent::KEYCODE_3}, 121 {'$', OHOS::MMI::KeyEvent::KEYCODE_4}, 122 {'%', OHOS::MMI::KeyEvent::KEYCODE_5}, 123 {'^', OHOS::MMI::KeyEvent::KEYCODE_6}, 124 {'&', OHOS::MMI::KeyEvent::KEYCODE_7}, 125 {'(', OHOS::MMI::KeyEvent::KEYCODE_9}, 126 {')', OHOS::MMI::KeyEvent::KEYCODE_0}, 127 {'+', OHOS::MMI::KeyEvent::KEYCODE_EQUALS}, 128 {'_', OHOS::MMI::KeyEvent::KEYCODE_MINUS}, 129 {':', OHOS::MMI::KeyEvent::KEYCODE_SEMICOLON}, 130 {'"', OHOS::MMI::KeyEvent::KEYCODE_APOSTROPHE}, 131 {'<', OHOS::MMI::KeyEvent::KEYCODE_COMMA}, 132 {'>', OHOS::MMI::KeyEvent::KEYCODE_PERIOD}, 133 {'?', OHOS::MMI::KeyEvent::KEYCODE_SLASH}, 134 {'{', OHOS::MMI::KeyEvent::KEYCODE_LEFT_BRACKET}, 135 {'}', OHOS::MMI::KeyEvent::KEYCODE_RIGHT_BRACKET}, 136 {'|', OHOS::MMI::KeyEvent::KEYCODE_BACKSLASH} 137 }; 138 139 static constexpr EventSpec WATCHED_EVENTS[] = { 140 {"Toast", WindowsContentChangeTypes::CONTENT_CHANGE_TYPE_SUBTREE, "toastShow"}, 141 {"AlertDialog", WindowsContentChangeTypes::CONTENT_CHANGE_TYPE_SUBTREE, "dialogShow"} 142 }; 143 GetWatchedEvent(const AccessibilityEventInfo & eventInfo)144 static std::string GetWatchedEvent(const AccessibilityEventInfo &eventInfo) 145 { 146 auto eventCounts = sizeof(WATCHED_EVENTS) / sizeof(EventSpec); 147 for (unsigned long index = 0; index < eventCounts; index++) { 148 if (WATCHED_EVENTS[index].componentTyep == eventInfo.GetComponentType() && 149 WATCHED_EVENTS[index].eventType == eventInfo.GetWindowContentChangeTypes()) { 150 LOG_W("Capture event: %{public}s", WATCHED_EVENTS[index].event.data()); 151 return string(WATCHED_EVENTS[index].event); 152 } 153 } 154 return "undefine"; 155 } 156 157 // UiEventMonitor instance. 158 static shared_ptr<UiEventMonitor> g_monitorInstance_ = make_shared<UiEventMonitor>(); 159 SetOnAbilityConnectCallback(function<void ()> onConnectCb)160 void UiEventMonitor::SetOnAbilityConnectCallback(function<void()> onConnectCb) 161 { 162 onConnectCallback_ = std::move(onConnectCb); 163 } 164 SetOnAbilityDisConnectCallback(function<void ()> onDisConnectCb)165 void UiEventMonitor::SetOnAbilityDisConnectCallback(function<void()> onDisConnectCb) 166 { 167 onDisConnectCallback_ = std::move(onDisConnectCb); 168 } 169 OnAbilityConnected()170 void UiEventMonitor::OnAbilityConnected() 171 { 172 if (onConnectCallback_ != nullptr) { 173 onConnectCallback_(); 174 } 175 } 176 OnAbilityDisconnected()177 void UiEventMonitor::OnAbilityDisconnected() 178 { 179 if (onDisConnectCallback_ != nullptr) { 180 onDisConnectCallback_(); 181 } 182 } 183 184 // the monitored events 185 static constexpr uint32_t EVENT_MASK = EventType::TYPE_VIEW_TEXT_UPDATE_EVENT | 186 EventType::TYPE_PAGE_STATE_UPDATE | 187 EventType::TYPE_PAGE_CONTENT_UPDATE | 188 EventType::TYPE_VIEW_SCROLLED_EVENT | 189 EventType::TYPE_WINDOW_UPDATE; 190 RegisterUiEventListener(std::shared_ptr<UiEventListener> listerner)191 void UiEventMonitor::RegisterUiEventListener(std::shared_ptr<UiEventListener> listerner) 192 { 193 listeners_.emplace_back(listerner); 194 } 195 OnAccessibilityEvent(const AccessibilityEventInfo & eventInfo)196 void UiEventMonitor::OnAccessibilityEvent(const AccessibilityEventInfo &eventInfo) 197 { 198 auto eventType = eventInfo.GetEventType(); 199 LOG_D("OnEvent:0x%{public}x", eventType); 200 auto capturedEvent = GetWatchedEvent(eventInfo); 201 if (eventType == Accessibility::EventType::TYPE_VIEW_SCROLLED_START) { 202 LOG_D("Capture scroll begin"); 203 scrollCompelete_.store(false); 204 lastScrollBeginEventMillis_.store(GetCurrentMillisecond()); 205 } 206 if (eventType == Accessibility::EventType::TYPE_VIEW_SCROLLED_EVENT) { 207 LOG_D("Capture scroll end"); 208 scrollCompelete_.store(true); 209 } 210 if (capturedEvent != "undefine") { 211 auto bundleName = eventInfo.GetBundleName(); 212 auto contentList = eventInfo.GetContentList(); 213 auto text = !contentList.empty() ? contentList[0] : ""; 214 auto type = eventInfo.GetComponentType(); 215 UiEventSourceInfo uiEventSourceInfo = {bundleName, text, type}; 216 for (auto &listener : listeners_) { 217 listener->OnEvent(capturedEvent, uiEventSourceInfo); 218 } 219 } 220 if ((eventInfo.GetEventType() & EVENT_MASK) > 0) { 221 lastEventMillis_.store(GetCurrentMillisecond()); 222 } 223 } 224 GetLastEventMillis()225 uint64_t UiEventMonitor::GetLastEventMillis() 226 { 227 if (lastEventMillis_.load() <= 0) { 228 lastEventMillis_.store(GetCurrentMillisecond()); 229 } 230 return lastEventMillis_.load(); 231 } 232 WaitScrollCompelete()233 void UiEventMonitor::WaitScrollCompelete() 234 { 235 if (scrollCompelete_.load()) { 236 return; 237 } 238 auto currentMs = GetCurrentMillisecond(); 239 if (lastScrollBeginEventMillis_.load() <= 0) { 240 lastScrollBeginEventMillis_.store(currentMs); 241 } 242 const auto idleThresholdMs = 10000; 243 static constexpr auto sliceMs = 10; 244 while (currentMs - lastScrollBeginEventMillis_.load() < idleThresholdMs) { 245 if (scrollCompelete_.load()) { 246 return; 247 } 248 this_thread::sleep_for(chrono::milliseconds(sliceMs)); 249 currentMs = GetCurrentMillisecond(); 250 } 251 LOG_E("wait for scrollEnd event timeout."); 252 scrollCompelete_.store(true); 253 return; 254 } 255 WaitEventIdle(uint32_t idleThresholdMs,uint32_t timeoutMs)256 bool UiEventMonitor::WaitEventIdle(uint32_t idleThresholdMs, uint32_t timeoutMs) 257 { 258 const auto currentMs = GetCurrentMillisecond(); 259 if (lastEventMillis_.load() <= 0) { 260 lastEventMillis_.store(currentMs); 261 } 262 if (currentMs - lastEventMillis_.load() >= idleThresholdMs) { 263 return true; 264 } 265 static constexpr auto sliceMs = 10; 266 this_thread::sleep_for(chrono::milliseconds(sliceMs)); 267 if (timeoutMs <= sliceMs) { 268 return false; 269 } 270 return WaitEventIdle(idleThresholdMs, timeoutMs - sliceMs); 271 } 272 SysUiController()273 SysUiController::SysUiController() : UiController() {} 274 ~SysUiController()275 SysUiController::~SysUiController() 276 { 277 DisConnectFromSysAbility(); 278 } 279 Initialize(ApiCallErr & error)280 bool SysUiController::Initialize(ApiCallErr &error) 281 { 282 return this->ConnectToSysAbility(error); 283 } 284 GetVisibleRect(Rect windowBounds,Accessibility::Rect nodeBounds)285 static Rect GetVisibleRect(Rect windowBounds, Accessibility::Rect nodeBounds) 286 { 287 auto leftX = nodeBounds.GetLeftTopXScreenPostion(); 288 auto topY = nodeBounds.GetLeftTopYScreenPostion(); 289 auto rightX = nodeBounds.GetRightBottomXScreenPostion(); 290 auto bottomY = nodeBounds.GetRightBottomYScreenPostion(); 291 Rect newBounds((leftX < windowBounds.left_) ? windowBounds.left_ : leftX, 292 (rightX > windowBounds.right_) ? windowBounds.right_ : rightX, 293 (topY < windowBounds.top_) ? windowBounds.top_ : topY, 294 (bottomY > windowBounds.bottom_) ? windowBounds.bottom_ : bottomY); 295 return newBounds; 296 } 297 InflateWindowInfo(AccessibilityWindowInfo & node,Window & info)298 static void InflateWindowInfo(AccessibilityWindowInfo& node, Window& info) 299 { 300 info.focused_ = node.IsFocused(); 301 info.actived_ = node.IsActive(); 302 info.decoratorEnabled_ = node.IsDecorEnable(); 303 // get bundle name by root node 304 AccessibilityElementInfo element; 305 LOG_D("Start Get Bundle Name by WindowId %{public}d", node.GetWindowId()); 306 if (AccessibilityUITestAbility::GetInstance()->GetRootByWindow(node, element) != RET_OK) { 307 LOG_E("Failed Get Bundle Name by WindowId %{public}d", node.GetWindowId()); 308 } else { 309 std::string app = element.GetBundleName(); 310 LOG_I("End Get Bundle Name by WindowId %{public}d, app is %{public}s", node.GetWindowId(), app.data()); 311 info.bundleName_ = app; 312 const auto foreAbility = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility(); 313 info.abilityName_ = (app == foreAbility.GetBundleName()) ? foreAbility.GetAbilityName() : ""; 314 info.pagePath_ = (app == foreAbility.GetBundleName()) ? element.GetPagePath() : ""; 315 } 316 info.mode_ = WindowMode::UNKNOWN; 317 const auto origMode = static_cast<OHOS::Rosen::WindowMode>(node.GetWindowMode()); 318 switch (origMode) { 319 case OHOS::Rosen::WindowMode::WINDOW_MODE_FULLSCREEN: 320 info.mode_ = WindowMode::FULLSCREEN; 321 break; 322 case OHOS::Rosen::WindowMode::WINDOW_MODE_SPLIT_PRIMARY: 323 info.mode_ = WindowMode::SPLIT_PRIMARY; 324 break; 325 case OHOS::Rosen::WindowMode::WINDOW_MODE_SPLIT_SECONDARY: 326 info.mode_ = WindowMode::SPLIT_SECONDARY; 327 break; 328 case OHOS::Rosen::WindowMode::WINDOW_MODE_FLOATING: 329 info.mode_ = WindowMode::FLOATING; 330 break; 331 case OHOS::Rosen::WindowMode::WINDOW_MODE_PIP: 332 info.mode_ = WindowMode::PIP; 333 break; 334 default: 335 info.mode_ = WindowMode::UNKNOWN; 336 break; 337 } 338 } 339 GetAamsWindowInfos(vector<AccessibilityWindowInfo> & windows)340 static bool GetAamsWindowInfos(vector<AccessibilityWindowInfo> &windows) 341 { 342 auto ability = AccessibilityUITestAbility::GetInstance(); 343 g_monitorInstance_->WaitScrollCompelete(); 344 if (ability->GetWindows(windows) != RET_OK) { 345 LOG_W("GetWindows from AccessibilityUITestAbility failed"); 346 return false; 347 } 348 sort(windows.begin(), windows.end(), [](auto &w1, auto &w2) -> bool { 349 return w1.GetWindowLayer() > w2.GetWindowLayer(); 350 }); 351 return true; 352 } 353 GetUiWindows(std::vector<Window> & out)354 void SysUiController::GetUiWindows(std::vector<Window> &out) 355 { 356 std::lock_guard<std::mutex> dumpLocker(dumpMtx); // disallow concurrent dumpUi 357 ApiCallErr error = ApiCallErr(NO_ERROR); 358 if (!connected_ && !ConnectToSysAbility(error)) { 359 LOG_E("%{public}s", error.message_.c_str()); 360 return; 361 } 362 vector<AccessibilityWindowInfo> windows; 363 LOG_D("Get Window root info"); 364 if (!GetAamsWindowInfos(windows)) { 365 return; 366 } 367 LOG_D("End Get Window root info"); 368 auto screenSize = GetDisplaySize(); 369 auto screenRect = Rect(0, screenSize.px_, 0, screenSize.py_); 370 std::vector<Rect> overplays; 371 // window wrapper 372 for (auto &win : windows) { 373 Rect winRectInScreen = GetVisibleRect(screenRect, win.GetRectInScreen()); 374 Rect visibleArea = winRectInScreen; 375 if (!RectAlgorithm::ComputeMaxVisibleRegion(winRectInScreen, overplays, visibleArea)) { 376 LOG_I("window is covered, windowId : %{public}d, layer is %{public}d", win.GetWindowId(), 377 win.GetWindowLayer()); 378 continue; 379 } 380 LOG_I("window is visible, windowId: %{public}d, active: %{public}d, focus: %{public}d, layer: %{public}d", 381 win.GetWindowId(), win.IsActive(), win.IsFocused(), win.GetWindowLayer()); 382 Window winWrapper{win.GetWindowId()}; 383 InflateWindowInfo(win, winWrapper); 384 winWrapper.bounds_ = winRectInScreen; 385 for (const auto &overWin : overplays) { 386 Rect intersectionRect{0, 0, 0, 0}; 387 if (RectAlgorithm::ComputeIntersection(winRectInScreen, overWin, intersectionRect)) { 388 winWrapper.invisibleBoundsVec_.emplace_back(overWin); 389 } 390 } 391 RectAlgorithm::ComputeMaxVisibleRegion(winWrapper.bounds_, overplays, winWrapper.visibleBounds_); 392 overplays.emplace_back(winRectInScreen); 393 out.emplace_back(std::move(winWrapper)); 394 } 395 } 396 GetWidgetsInWindow(const Window & winInfo,unique_ptr<ElementNodeIterator> & elementIterator)397 bool SysUiController::GetWidgetsInWindow(const Window &winInfo, unique_ptr<ElementNodeIterator> &elementIterator) 398 { 399 std::lock_guard<std::mutex> dumpLocker(dumpMtx); // disallow concurrent dumpUi 400 if (!connected_) { 401 LOG_W("Connect to AccessibilityUITestAbility failed"); 402 return false; 403 } 404 std::vector<AccessibilityElementInfo> elementInfos; 405 AccessibilityWindowInfo window; 406 LOG_D("Get Window by WindowId %{public}d", winInfo.id_); 407 if (AccessibilityUITestAbility::GetInstance()->GetWindow(winInfo.id_, window) != RET_OK) { 408 LOG_E("GetWindowInfo failed, windowId: %{public}d", winInfo.id_); 409 return false; 410 } 411 LOG_D("Start Get nodes from window by WindowId %{public}d", winInfo.id_); 412 if (AccessibilityUITestAbility::GetInstance()->GetRootByWindowBatch(window, elementInfos) != RET_OK) { 413 LOG_E("GetRootByWindowBatch failed, windowId: %{public}d", winInfo.id_); 414 return false; 415 } else { 416 LOG_I("End Get nodes from window by WindowId %{public}d, node size is %{public}zu, appId: %{public}s", 417 winInfo.id_, elementInfos.size(), winInfo.bundleName_.data()); 418 elementIterator = std::make_unique<ElementNodeIteratorImpl>(elementInfos); 419 } 420 return true; 421 } 422 AddPinterItems(PointerEvent & event,const vector<pair<bool,Point>> & fingerStatus,uint32_t currentFinger)423 static void AddPinterItems(PointerEvent &event, const vector<pair<bool, Point>> &fingerStatus, 424 uint32_t currentFinger) 425 { 426 PointerEvent::PointerItem pinterItem1; 427 pinterItem1.SetPointerId(currentFinger); 428 pinterItem1.SetOriginPointerId(currentFinger); 429 pinterItem1.SetDisplayX(fingerStatus[currentFinger].second.px_); 430 pinterItem1.SetDisplayY(fingerStatus[currentFinger].second.py_); 431 pinterItem1.SetPressed(fingerStatus[currentFinger].first); 432 event.UpdatePointerItem(currentFinger, pinterItem1); 433 // update pinterItem of other fingers which in pressed state. 434 for (uint32_t index = 0; index < fingerStatus.size(); index++) { 435 if (index == currentFinger) { 436 continue; 437 } 438 if (fingerStatus[index].first) { 439 PointerEvent::PointerItem pinterItem; 440 pinterItem.SetPointerId(index); 441 pinterItem.SetOriginPointerId(index); 442 pinterItem.SetDisplayX(fingerStatus[index].second.px_); 443 pinterItem.SetDisplayY(fingerStatus[index].second.py_); 444 pinterItem.SetPressed(true); 445 event.UpdatePointerItem(index, pinterItem); 446 } 447 } 448 } 449 InjectTouchEventSequence(const PointerMatrix & events) const450 void SysUiController::InjectTouchEventSequence(const PointerMatrix &events) const 451 { 452 // fingerStatus stores the press status and coordinates of each finger. 453 vector<pair<bool, Point>> fingerStatus(events.GetFingers(), make_pair(false, Point(0,0))); 454 for (uint32_t step = 0; step < events.GetSteps(); step++) { 455 auto pointerEvent = PointerEvent::Create(); 456 if (pointerEvent == nullptr) { 457 LOG_E("Creat PointerEvent failed."); 458 return; 459 } 460 for (uint32_t finger = 0; finger < events.GetFingers(); finger++) { 461 bool isPressed = events.At(finger, step).stage_ != ActionStage::UP; 462 fingerStatus[finger] = make_pair(isPressed, events.At(finger, step).point_); 463 pointerEvent->SetPointerId(finger); 464 switch (events.At(finger, step).stage_) { 465 case ActionStage::DOWN: 466 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_DOWN); 467 break; 468 case ActionStage::MOVE: 469 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_MOVE); 470 break; 471 case ActionStage::UP: 472 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_UP); 473 break; 474 default: 475 break; 476 } 477 AddPinterItems(*pointerEvent, fingerStatus, finger); 478 pointerEvent->SetSourceType(PointerEvent::SOURCE_TYPE_TOUCHSCREEN); 479 DisplayManager &displayMgr = DisplayManager::GetInstance(); 480 pointerEvent->SetTargetDisplayId(displayMgr.GetDefaultDisplayId()); 481 InputManager::GetInstance()->SimulateInputEvent(pointerEvent); 482 if (events.At(finger, step).holdMs_ > 0) { 483 this_thread::sleep_for(chrono::milliseconds(events.At(finger, step).holdMs_)); 484 } 485 } 486 } 487 } 488 SetMousePointerItemAttr(const MouseEvent & event,PointerEvent::PointerItem & item)489 static void SetMousePointerItemAttr(const MouseEvent &event, PointerEvent::PointerItem &item) 490 { 491 item.SetPointerId(0); 492 item.SetOriginPointerId(0); 493 item.SetToolType(PointerEvent::TOOL_TYPE_MOUSE); 494 item.SetDisplayX(event.point_.px_); 495 item.SetDisplayY(event.point_.py_); 496 item.SetPressed(false); 497 item.SetDownTime(0); 498 } 499 InjectMouseEvent(const MouseEvent & event) const500 void SysUiController::InjectMouseEvent(const MouseEvent &event) const 501 { 502 auto pointerEvent = PointerEvent::Create(); 503 if (pointerEvent == nullptr) { 504 return; 505 } 506 PointerEvent::PointerItem item; 507 pointerEvent->SetSourceType(PointerEvent::SOURCE_TYPE_MOUSE); 508 pointerEvent->SetPointerId(0); 509 pointerEvent->SetButtonId(event.button_); 510 SetMousePointerItemAttr(event, item); 511 constexpr double axialValue = 15; 512 static bool flag = true; 513 auto injectAxialValue = axialValue; 514 switch (event.stage_) { 515 case ActionStage::DOWN: 516 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_BUTTON_DOWN); 517 pointerEvent->SetButtonId(event.button_); 518 pointerEvent->SetButtonPressed(event.button_); 519 item.SetPressed(true); 520 break; 521 case ActionStage::MOVE: 522 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_MOVE); 523 break; 524 case ActionStage::UP: 525 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_BUTTON_UP); 526 pointerEvent->SetButtonId(event.button_); 527 pointerEvent->SetButtonPressed(event.button_); 528 break; 529 case ActionStage::AXIS_UP: 530 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_BEGIN); 531 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, -axialValue); 532 flag = false; 533 break; 534 case ActionStage::AXIS_DOWN: 535 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_BEGIN); 536 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, axialValue); 537 flag = true; 538 break; 539 case ActionStage::AXIS_STOP: 540 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_END); 541 injectAxialValue = flag ? axialValue : -axialValue; 542 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, injectAxialValue); 543 break; 544 default: 545 break; 546 } 547 pointerEvent->AddPointerItem(item); 548 InputManager::GetInstance()->SimulateInputEvent(pointerEvent); 549 this_thread::sleep_for(chrono::milliseconds(event.holdMs_)); 550 } 551 InjectMouseEventSequence(const vector<MouseEvent> & events) const552 void SysUiController::InjectMouseEventSequence(const vector<MouseEvent> &events) const 553 { 554 for (auto &event : events) { 555 auto keyEvents = event.keyEvents_; 556 if (!keyEvents.empty() && keyEvents.front().stage_ == ActionStage::DOWN) { 557 InjectKeyEventSequence(keyEvents); 558 InjectMouseEvent(event); 559 } else { 560 InjectMouseEvent(event); 561 InjectKeyEventSequence(keyEvents); 562 } 563 } 564 } 565 InjectKeyEventSequence(const vector<KeyEvent> & events) const566 void SysUiController::InjectKeyEventSequence(const vector<KeyEvent> &events) const 567 { 568 static vector<int32_t> downKeys; 569 for (auto &event : events) { 570 if (event.code_ == KEYCODE_NONE) { 571 continue; 572 } 573 auto keyEvent = OHOS::MMI::KeyEvent::Create(); 574 if (keyEvent == nullptr) { 575 LOG_E("Creat KeyEvent failed."); 576 return; 577 } 578 if (event.stage_ == ActionStage::UP) { 579 auto iter = std::find(downKeys.begin(), downKeys.end(), event.code_); 580 if (iter == downKeys.end()) { 581 LOG_W("Cannot release a not-pressed key: %{public}d", event.code_); 582 continue; 583 } 584 downKeys.erase(iter); 585 keyEvent->SetKeyCode(event.code_); 586 keyEvent->SetKeyAction(OHOS::MMI::KeyEvent::KEY_ACTION_UP); 587 OHOS::MMI::KeyEvent::KeyItem keyItem; 588 keyItem.SetKeyCode(event.code_); 589 keyItem.SetPressed(true); 590 keyEvent->AddKeyItem(keyItem); 591 InputManager::GetInstance()->SimulateInputEvent(keyEvent); 592 } else { 593 downKeys.push_back(event.code_); 594 for (auto downKey : downKeys) { 595 keyEvent->SetKeyCode(downKey); 596 keyEvent->SetKeyAction(OHOS::MMI::KeyEvent::KEY_ACTION_DOWN); 597 OHOS::MMI::KeyEvent::KeyItem keyItem; 598 keyItem.SetKeyCode(downKey); 599 keyItem.SetPressed(true); 600 keyEvent->AddKeyItem(keyItem); 601 } 602 InputManager::GetInstance()->SimulateInputEvent(keyEvent); 603 if (event.holdMs_ > 0) { 604 this_thread::sleep_for(chrono::milliseconds(event.holdMs_)); 605 } 606 } 607 } 608 // check not released keys 609 for (auto downKey : downKeys) { 610 LOG_W("Key event sequence injections done with not-released key: %{public}d", downKey); 611 } 612 } 613 PutTextToClipboard(string_view text) const614 void SysUiController::PutTextToClipboard(string_view text) const 615 { 616 OHOS::testserver::TestServerClient::GetInstance().SetPasteData(string(text)); 617 } 618 IsWorkable() const619 bool SysUiController::IsWorkable() const 620 { 621 return connected_; 622 } 623 GetCharKeyCode(char ch,int32_t & code,int32_t & ctrlCode) const624 bool SysUiController::GetCharKeyCode(char ch, int32_t &code, int32_t &ctrlCode) const 625 { 626 ctrlCode = KEYCODE_NONE; 627 if (ch >= 'a' && ch <= 'z') { 628 code = OHOS::MMI::KeyEvent::KEYCODE_A + static_cast<int32_t>(ch - 'a'); 629 } else if (ch >= 'A' && ch <= 'Z') { 630 ctrlCode = OHOS::MMI::KeyEvent::KEYCODE_SHIFT_LEFT; 631 code = OHOS::MMI::KeyEvent::KEYCODE_A + static_cast<int32_t>(ch - 'A'); 632 } else if (ch >= '0' && ch <= '9') { 633 code = OHOS::MMI::KeyEvent::KEYCODE_0 + static_cast<int32_t>(ch - '0'); 634 } else if (SingleKeySymbalMap.find(ch) != SingleKeySymbalMap.end()) { 635 code = SingleKeySymbalMap.find(ch)->second; 636 } else if (MultiKeySymbalMap.find(ch) != MultiKeySymbalMap.end()) { 637 ctrlCode = OHOS::MMI::KeyEvent::KEYCODE_SHIFT_LEFT; 638 code = MultiKeySymbalMap.find(ch)->second; 639 } else { 640 return false; 641 } 642 return true; 643 } 644 TakeScreenCap(int32_t fd,std::stringstream & errReceiver,Rect rect) const645 bool SysUiController::TakeScreenCap(int32_t fd, std::stringstream &errReceiver, Rect rect) const 646 { 647 DisplayManager &displayMgr = DisplayManager::GetInstance(); 648 // get PixelMap from DisplayManager API 649 shared_ptr<PixelMap> pixelMap; 650 if (rect.GetWidth() == 0) { 651 pixelMap = displayMgr.GetScreenshot(displayMgr.GetDefaultDisplayId()); 652 } else { 653 Media::Rect region = {.left = rect.left_, .top = rect.top_, 654 .width = rect.right_ - rect.left_, .height = rect.bottom_ - rect.top_}; 655 Media::Size size = {.width = rect.right_ - rect.left_, .height = rect.bottom_ - rect.top_}; 656 pixelMap = displayMgr.GetScreenshot(displayMgr.GetDefaultDisplayId(), region, size, 0); 657 } 658 if (pixelMap == nullptr) { 659 errReceiver << "Failed to get display pixelMap"; 660 return false; 661 } 662 FILE *fp = fdopen(fd, "wb"); 663 if (fp == nullptr) { 664 perror("File opening failed"); 665 errReceiver << "File opening failed"; 666 return false; 667 } 668 png_structp pngStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 669 if (pngStruct == nullptr) { 670 fclose(fp); 671 return false; 672 } 673 png_infop pngInfo = png_create_info_struct(pngStruct); 674 if (pngInfo == nullptr) { 675 fclose(fp); 676 png_destroy_write_struct(&pngStruct, nullptr); 677 return false; 678 } 679 png_init_io(pngStruct, fp); 680 auto width = static_cast<uint32_t>(pixelMap->GetWidth()); 681 auto height = static_cast<uint32_t>(pixelMap->GetHeight()); 682 auto data = pixelMap->GetPixels(); 683 auto stride = static_cast<uint32_t>(pixelMap->GetRowBytes()); 684 // set png header 685 static constexpr int bitmapDepth = 8; 686 png_set_IHDR(pngStruct, pngInfo, width, height, bitmapDepth, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, 687 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 688 png_set_packing(pngStruct); // set packing info 689 png_write_info(pngStruct, pngInfo); // write to header 690 for (uint32_t column = 0; column < height; column++) { 691 png_write_row(pngStruct, data + (column * stride)); 692 } 693 // free/close 694 png_write_end(pngStruct, pngInfo); 695 png_destroy_write_struct(&pngStruct, &pngInfo); 696 (void)fclose(fp); 697 return true; 698 } 699 ConnectToSysAbility(ApiCallErr & error)700 bool SysUiController::ConnectToSysAbility(ApiCallErr &error) 701 { 702 if (connected_) { 703 return true; 704 } 705 mutex mtx; 706 unique_lock<mutex> uLock(mtx); 707 std::shared_ptr<condition_variable> condition = make_shared<condition_variable>(); 708 auto onConnectCallback = [condition]() { 709 LOG_I("Success connect to AccessibilityUITestAbility"); 710 condition->notify_all(); 711 }; 712 auto onDisConnectCallback = [this]() { this->connected_ = false; }; 713 if (g_monitorInstance_ == nullptr) { 714 g_monitorInstance_ = make_shared<UiEventMonitor>(); 715 } 716 g_monitorInstance_->SetOnAbilityConnectCallback(onConnectCallback); 717 g_monitorInstance_->SetOnAbilityDisConnectCallback(onDisConnectCallback); 718 auto ability = AccessibilityUITestAbility::GetInstance(); 719 if (ability->RegisterAbilityListener(g_monitorInstance_) != RET_OK) { 720 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS, REGISTER_LISTENER_FAILED"); 721 return false; 722 } 723 auto ret = ability->Connect(); 724 LOG_I("Connect to AAMS, result: %{public}d", ret); 725 if (ret != RET_OK) { 726 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS"); 727 if (ret == RET_ERR_CONNECTION_EXIST) { 728 error.message_ += ", RET_ERR_CONNECTION_EXIST"; 729 } else { 730 error.message_ += ", RET_ERR_AAMS"; 731 } 732 return false; 733 } 734 const auto timeout = chrono::milliseconds(3000); 735 if (condition->wait_for(uLock, timeout) == cv_status::timeout) { 736 LOG_E("Wait connection to AccessibilityUITestAbility timed out"); 737 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS, RET_TIMEOUT"); 738 return false; 739 } 740 connected_ = true; 741 return true; 742 } 743 RegisterUiEventListener(std::shared_ptr<UiEventListener> listener) const744 void SysUiController::RegisterUiEventListener(std::shared_ptr<UiEventListener> listener) const 745 { 746 g_monitorInstance_->RegisterUiEventListener(listener); 747 } 748 WaitForUiSteady(uint32_t idleThresholdMs,uint32_t timeoutMs) const749 bool SysUiController::WaitForUiSteady(uint32_t idleThresholdMs, uint32_t timeoutMs) const 750 { 751 return g_monitorInstance_->WaitEventIdle(idleThresholdMs, timeoutMs); 752 } 753 DisConnectFromSysAbility()754 void SysUiController::DisConnectFromSysAbility() 755 { 756 if (!connected_ || g_monitorInstance_ == nullptr) { 757 return; 758 } 759 connected_ = false; 760 mutex mtx; 761 unique_lock<mutex> uLock(mtx); 762 condition_variable condition; 763 auto onDisConnectCallback = [&condition]() { 764 LOG_I("Success disconnect from AccessibilityUITestAbility"); 765 condition.notify_all(); 766 }; 767 g_monitorInstance_->SetOnAbilityDisConnectCallback(onDisConnectCallback); 768 auto ability = AccessibilityUITestAbility::GetInstance(); 769 LOG_I("Start disconnect from AccessibilityUITestAbility"); 770 if (ability->Disconnect() != RET_OK) { 771 LOG_E("Failed to disconnect from AccessibilityUITestAbility"); 772 return; 773 } 774 const auto timeout = chrono::milliseconds(200); 775 if (condition.wait_for(uLock, timeout) == cv_status::timeout) { 776 LOG_E("Wait disconnection from AccessibilityUITestAbility timed out"); 777 return; 778 } 779 } 780 SetDisplayRotation(DisplayRotation rotation) const781 void SysUiController::SetDisplayRotation(DisplayRotation rotation) const 782 { 783 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 784 if (display == nullptr) { 785 LOG_E("DisplayManager init fail"); 786 return; 787 } 788 auto screenId = display->GetScreenId(); 789 ScreenManager &screenMgr = ScreenManager::GetInstance(); 790 DCHECK(screenMgr); 791 bool isLocked = false; 792 screenMgr.IsScreenRotationLocked(isLocked); 793 if (isLocked) { 794 screenMgr.SetScreenRotationLocked(false); 795 } 796 auto screen = screenMgr.GetScreenById(screenId); 797 if (screen == nullptr) { 798 LOG_E("ScreenManager init fail"); 799 return; 800 } 801 switch (rotation) { 802 case ROTATION_0 : 803 screen->SetOrientation(Orientation::VERTICAL); 804 break; 805 case ROTATION_90 : 806 screen->SetOrientation(Orientation::HORIZONTAL); 807 break; 808 case ROTATION_180 : 809 screen->SetOrientation(Orientation::REVERSE_VERTICAL); 810 break; 811 case ROTATION_270 : 812 screen->SetOrientation(Orientation::REVERSE_HORIZONTAL); 813 break; 814 default : 815 break; 816 } 817 } 818 GetDisplayRotation() const819 DisplayRotation SysUiController::GetDisplayRotation() const 820 { 821 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 822 if (display == nullptr) { 823 LOG_E("DisplayManager init fail"); 824 return DisplayRotation::ROTATION_0; 825 } 826 auto rotation = (DisplayRotation)display->GetRotation(); 827 return rotation; 828 } 829 SetDisplayRotationEnabled(bool enabled) const830 void SysUiController::SetDisplayRotationEnabled(bool enabled) const 831 { 832 ScreenManager &screenMgr = ScreenManager::GetInstance(); 833 DCHECK(screenMgr); 834 screenMgr.SetScreenRotationLocked(!enabled); 835 } 836 GetDisplaySize() const837 Point SysUiController::GetDisplaySize() const 838 { 839 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 840 if (display == nullptr) { 841 LOG_E("DisplayManager init fail"); 842 return {0, 0}; 843 } 844 auto width = display->GetWidth(); 845 auto height = display->GetHeight(); 846 LOG_D("GetDisplaysize, width: %{public}d, height: %{public}d", width, height); 847 Point result(width, height); 848 return result; 849 } 850 GetDisplayDensity() const851 Point SysUiController::GetDisplayDensity() const 852 { 853 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 854 if (display == nullptr) { 855 LOG_E("DisplayManager init fail"); 856 return {0, 0}; 857 } 858 auto rate = display->GetVirtualPixelRatio(); 859 Point displaySize = GetDisplaySize(); 860 Point result(displaySize.px_ * rate, displaySize.py_ * rate); 861 return result; 862 } 863 IsScreenOn() const864 bool SysUiController::IsScreenOn() const 865 { 866 DisplayManager &displayMgr = DisplayManager::GetInstance(); 867 DCHECK(displayMgr); 868 auto displayId = displayMgr.GetDefaultDisplayId(); 869 auto state = displayMgr.GetDisplayState(displayId); 870 return (state != DisplayState::OFF); 871 } 872 873 class OnSaLoadCallback : public SystemAbilityLoadCallbackStub { 874 public: OnSaLoadCallback(mutex & mutex)875 explicit OnSaLoadCallback(mutex &mutex): mutex_(mutex) {}; ~OnSaLoadCallback()876 ~OnSaLoadCallback() {}; OnLoadSystemAbilitySuccess(int32_t systemAbilityId,const sptr<IRemoteObject> & remoteObject)877 void OnLoadSystemAbilitySuccess(int32_t systemAbilityId, const sptr<IRemoteObject>& remoteObject) override 878 { 879 if (systemAbilityId == OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID) { 880 remoteObject_ = remoteObject; 881 mutex_.unlock(); 882 } 883 } OnLoadSystemAbilityFail(int32_t systemAbilityId)884 void OnLoadSystemAbilityFail(int32_t systemAbilityId) override 885 { 886 if (systemAbilityId == OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID) { 887 mutex_.unlock(); 888 } 889 } 890 GetSaObject()891 sptr<IRemoteObject> GetSaObject() 892 { 893 return remoteObject_; 894 } 895 896 private: 897 mutex &mutex_; 898 sptr<IRemoteObject> remoteObject_ = nullptr; 899 }; 900 CreateHidumperCmd(const std::string & windowId,vector<u16string> & result)901 static void CreateHidumperCmd(const std::string &windowId, vector<u16string> &result) 902 { 903 result.emplace_back(u"hidumper"); 904 result.emplace_back(u"-s"); 905 result.emplace_back(u"WindowManagerService"); 906 result.emplace_back(u"-a"); 907 auto winIdInUtf16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(windowId); 908 auto arg = u16string(u"-w ").append(winIdInUtf16).append(u" -default -lastpage"); 909 result.emplace_back(move(arg)); 910 } 911 GetHidumperInfo(std::string windowId,char ** buf,size_t & len)912 void SysUiController::GetHidumperInfo(std::string windowId, char **buf, size_t &len) 913 { 914 #ifdef HIDUMPER_ENABLED 915 auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 916 // wati SA start 917 constexpr auto delayMs = 2000; 918 this_thread::sleep_for(chrono::milliseconds(delayMs)); 919 if (sam == nullptr) { 920 LOG_E("Get samgr failed"); 921 return; 922 } 923 auto remoteObject = sam->CheckSystemAbility(OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID); 924 if (remoteObject == nullptr) { 925 mutex lock; 926 lock.lock(); 927 sptr<OnSaLoadCallback> loadCallback = new OnSaLoadCallback(lock); 928 if (sam->LoadSystemAbility(OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID, loadCallback) != ERR_OK) { 929 LOG_E("Schedule LoadSystemAbility failed"); 930 lock.unlock(); 931 return; 932 } 933 LOG_E("Schedule LoadSystemAbility succeed"); 934 lock.unlock(); 935 remoteObject = loadCallback->GetSaObject(); 936 LOG_E("LoadSystemAbility callbacked, result = %{public}s", remoteObject == nullptr ? "FAIL" : "SUCCESS"); 937 } 938 if (remoteObject == nullptr) { 939 LOG_E("remoteObject is null"); 940 return; 941 } 942 // run dump command 943 sptr<IDumpBroker> client = iface_cast<IDumpBroker>(remoteObject); 944 if (client == nullptr) { 945 LOG_E("IDumpBroker converts failed"); 946 return; 947 } 948 auto fd = memfd_create("dummy_file", 2); 949 ftruncate(fd, 0); 950 vector<u16string> args; 951 CreateHidumperCmd(windowId, args); 952 client->Request(args, fd); 953 auto size = lseek(fd, 0, SEEK_END); 954 char *tempBuf = new char[size + 1]; 955 lseek(fd, 0, SEEK_SET); 956 read(fd, tempBuf, size); 957 *buf = tempBuf; 958 len = size; 959 close(fd); 960 #else 961 *buf = nullptr; 962 len = 0; 963 #endif 964 } 965 } // namespace OHOS::uitest