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.SetRawDisplayX(fingerStatus[currentFinger].second.px_); 432 pinterItem1.SetRawDisplayY(fingerStatus[currentFinger].second.py_); 433 pinterItem1.SetPressed(fingerStatus[currentFinger].first); 434 event.UpdatePointerItem(currentFinger, pinterItem1); 435 // update pinterItem of other fingers which in pressed state. 436 for (uint32_t index = 0; index < fingerStatus.size(); index++) { 437 if (index == currentFinger) { 438 continue; 439 } 440 if (fingerStatus[index].first) { 441 PointerEvent::PointerItem pinterItem; 442 pinterItem.SetPointerId(index); 443 pinterItem.SetOriginPointerId(index); 444 pinterItem.SetDisplayX(fingerStatus[index].second.px_); 445 pinterItem.SetDisplayY(fingerStatus[index].second.py_); 446 pinterItem.SetRawDisplayX(fingerStatus[index].second.px_); 447 pinterItem.SetRawDisplayY(fingerStatus[index].second.py_); 448 pinterItem.SetPressed(true); 449 event.UpdatePointerItem(index, pinterItem); 450 } 451 } 452 } 453 InjectTouchEventSequence(const PointerMatrix & events) const454 void SysUiController::InjectTouchEventSequence(const PointerMatrix &events) const 455 { 456 // fingerStatus stores the press status and coordinates of each finger. 457 vector<pair<bool, Point>> fingerStatus(events.GetFingers(), make_pair(false, Point(0,0))); 458 for (uint32_t step = 0; step < events.GetSteps(); step++) { 459 auto pointerEvent = PointerEvent::Create(); 460 if (pointerEvent == nullptr) { 461 LOG_E("Creat PointerEvent failed."); 462 return; 463 } 464 for (uint32_t finger = 0; finger < events.GetFingers(); finger++) { 465 bool isPressed = events.At(finger, step).stage_ != ActionStage::UP; 466 fingerStatus[finger] = make_pair(isPressed, events.At(finger, step).point_); 467 pointerEvent->SetPointerId(finger); 468 switch (events.At(finger, step).stage_) { 469 case ActionStage::DOWN: 470 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_DOWN); 471 break; 472 case ActionStage::MOVE: 473 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_MOVE); 474 break; 475 case ActionStage::UP: 476 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_UP); 477 break; 478 default: 479 break; 480 } 481 AddPinterItems(*pointerEvent, fingerStatus, finger); 482 pointerEvent->SetSourceType(PointerEvent::SOURCE_TYPE_TOUCHSCREEN); 483 DisplayManager &displayMgr = DisplayManager::GetInstance(); 484 pointerEvent->SetTargetDisplayId(displayMgr.GetDefaultDisplayId()); 485 InputManager::GetInstance()->SimulateInputEvent(pointerEvent); 486 if (events.At(finger, step).holdMs_ > 0) { 487 this_thread::sleep_for(chrono::milliseconds(events.At(finger, step).holdMs_)); 488 } 489 } 490 } 491 } 492 SetMousePointerItemAttr(const MouseEvent & event,PointerEvent::PointerItem & item)493 static void SetMousePointerItemAttr(const MouseEvent &event, PointerEvent::PointerItem &item) 494 { 495 item.SetPointerId(0); 496 item.SetOriginPointerId(0); 497 item.SetToolType(PointerEvent::TOOL_TYPE_MOUSE); 498 item.SetDisplayX(event.point_.px_); 499 item.SetDisplayY(event.point_.py_); 500 item.SetRawDisplayX(event.point_.px_); 501 item.SetRawDisplayY(event.point_.py_); 502 item.SetPressed(false); 503 item.SetDownTime(0); 504 } 505 InjectMouseEvent(const MouseEvent & event) const506 void SysUiController::InjectMouseEvent(const MouseEvent &event) const 507 { 508 auto pointerEvent = PointerEvent::Create(); 509 if (pointerEvent == nullptr) { 510 return; 511 } 512 PointerEvent::PointerItem item; 513 pointerEvent->SetSourceType(PointerEvent::SOURCE_TYPE_MOUSE); 514 pointerEvent->SetPointerId(0); 515 pointerEvent->SetButtonId(event.button_); 516 SetMousePointerItemAttr(event, item); 517 constexpr double axialValue = 15; 518 static bool flag = true; 519 auto injectAxialValue = axialValue; 520 switch (event.stage_) { 521 case ActionStage::DOWN: 522 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_BUTTON_DOWN); 523 pointerEvent->SetButtonId(event.button_); 524 pointerEvent->SetButtonPressed(event.button_); 525 item.SetPressed(true); 526 break; 527 case ActionStage::MOVE: 528 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_MOVE); 529 break; 530 case ActionStage::UP: 531 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_BUTTON_UP); 532 pointerEvent->SetButtonId(event.button_); 533 pointerEvent->SetButtonPressed(event.button_); 534 break; 535 case ActionStage::AXIS_UP: 536 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_BEGIN); 537 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, -axialValue); 538 flag = false; 539 break; 540 case ActionStage::AXIS_DOWN: 541 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_BEGIN); 542 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, axialValue); 543 flag = true; 544 break; 545 case ActionStage::AXIS_STOP: 546 pointerEvent->SetPointerAction(OHOS::MMI::PointerEvent::POINTER_ACTION_AXIS_END); 547 injectAxialValue = flag ? axialValue : -axialValue; 548 pointerEvent->SetAxisValue(OHOS::MMI::PointerEvent::AXIS_TYPE_SCROLL_VERTICAL, injectAxialValue); 549 break; 550 default: 551 break; 552 } 553 pointerEvent->AddPointerItem(item); 554 InputManager::GetInstance()->SimulateInputEvent(pointerEvent); 555 this_thread::sleep_for(chrono::milliseconds(event.holdMs_)); 556 } 557 InjectMouseEventSequence(const vector<MouseEvent> & events) const558 void SysUiController::InjectMouseEventSequence(const vector<MouseEvent> &events) const 559 { 560 for (auto &event : events) { 561 auto keyEvents = event.keyEvents_; 562 if (!keyEvents.empty() && keyEvents.front().stage_ == ActionStage::DOWN) { 563 InjectKeyEventSequence(keyEvents); 564 InjectMouseEvent(event); 565 } else { 566 InjectMouseEvent(event); 567 InjectKeyEventSequence(keyEvents); 568 } 569 } 570 } 571 InjectKeyEventSequence(const vector<KeyEvent> & events) const572 void SysUiController::InjectKeyEventSequence(const vector<KeyEvent> &events) const 573 { 574 static vector<int32_t> downKeys; 575 for (auto &event : events) { 576 if (event.code_ == KEYCODE_NONE) { 577 continue; 578 } 579 auto keyEvent = OHOS::MMI::KeyEvent::Create(); 580 if (keyEvent == nullptr) { 581 LOG_E("Creat KeyEvent failed."); 582 return; 583 } 584 if (event.stage_ == ActionStage::UP) { 585 auto iter = std::find(downKeys.begin(), downKeys.end(), event.code_); 586 if (iter == downKeys.end()) { 587 LOG_W("Cannot release a not-pressed key: %{public}d", event.code_); 588 continue; 589 } 590 downKeys.erase(iter); 591 keyEvent->SetKeyCode(event.code_); 592 keyEvent->SetKeyAction(OHOS::MMI::KeyEvent::KEY_ACTION_UP); 593 OHOS::MMI::KeyEvent::KeyItem keyItem; 594 keyItem.SetKeyCode(event.code_); 595 keyItem.SetPressed(true); 596 keyEvent->AddKeyItem(keyItem); 597 InputManager::GetInstance()->SimulateInputEvent(keyEvent); 598 } else { 599 downKeys.push_back(event.code_); 600 for (auto downKey : downKeys) { 601 keyEvent->SetKeyCode(downKey); 602 keyEvent->SetKeyAction(OHOS::MMI::KeyEvent::KEY_ACTION_DOWN); 603 OHOS::MMI::KeyEvent::KeyItem keyItem; 604 keyItem.SetKeyCode(downKey); 605 keyItem.SetPressed(true); 606 keyEvent->AddKeyItem(keyItem); 607 } 608 InputManager::GetInstance()->SimulateInputEvent(keyEvent); 609 if (event.holdMs_ > 0) { 610 this_thread::sleep_for(chrono::milliseconds(event.holdMs_)); 611 } 612 } 613 } 614 // check not released keys 615 for (auto downKey : downKeys) { 616 LOG_W("Key event sequence injections done with not-released key: %{public}d", downKey); 617 } 618 } 619 PutTextToClipboard(string_view text) const620 void SysUiController::PutTextToClipboard(string_view text) const 621 { 622 OHOS::testserver::TestServerClient::GetInstance().SetPasteData(string(text)); 623 } 624 IsWorkable() const625 bool SysUiController::IsWorkable() const 626 { 627 return connected_; 628 } 629 GetCharKeyCode(char ch,int32_t & code,int32_t & ctrlCode) const630 bool SysUiController::GetCharKeyCode(char ch, int32_t &code, int32_t &ctrlCode) const 631 { 632 ctrlCode = KEYCODE_NONE; 633 if (ch >= 'a' && ch <= 'z') { 634 code = OHOS::MMI::KeyEvent::KEYCODE_A + static_cast<int32_t>(ch - 'a'); 635 } else if (ch >= 'A' && ch <= 'Z') { 636 ctrlCode = OHOS::MMI::KeyEvent::KEYCODE_SHIFT_LEFT; 637 code = OHOS::MMI::KeyEvent::KEYCODE_A + static_cast<int32_t>(ch - 'A'); 638 } else if (ch >= '0' && ch <= '9') { 639 code = OHOS::MMI::KeyEvent::KEYCODE_0 + static_cast<int32_t>(ch - '0'); 640 } else if (SingleKeySymbalMap.find(ch) != SingleKeySymbalMap.end()) { 641 code = SingleKeySymbalMap.find(ch)->second; 642 } else if (MultiKeySymbalMap.find(ch) != MultiKeySymbalMap.end()) { 643 ctrlCode = OHOS::MMI::KeyEvent::KEYCODE_SHIFT_LEFT; 644 code = MultiKeySymbalMap.find(ch)->second; 645 } else { 646 return false; 647 } 648 return true; 649 } 650 TakeScreenCap(int32_t fd,std::stringstream & errReceiver,Rect rect) const651 bool SysUiController::TakeScreenCap(int32_t fd, std::stringstream &errReceiver, Rect rect) const 652 { 653 DisplayManager &displayMgr = DisplayManager::GetInstance(); 654 // get PixelMap from DisplayManager API 655 shared_ptr<PixelMap> pixelMap; 656 if (rect.GetWidth() == 0) { 657 pixelMap = displayMgr.GetScreenshot(displayMgr.GetDefaultDisplayId()); 658 } else { 659 Media::Rect region = {.left = rect.left_, .top = rect.top_, 660 .width = rect.right_ - rect.left_, .height = rect.bottom_ - rect.top_}; 661 Media::Size size = {.width = rect.right_ - rect.left_, .height = rect.bottom_ - rect.top_}; 662 pixelMap = displayMgr.GetScreenshot(displayMgr.GetDefaultDisplayId(), region, size, 0); 663 } 664 if (pixelMap == nullptr) { 665 errReceiver << "Failed to get display pixelMap"; 666 return false; 667 } 668 FILE *fp = fdopen(fd, "wb"); 669 if (fp == nullptr) { 670 perror("File opening failed"); 671 errReceiver << "File opening failed"; 672 return false; 673 } 674 png_structp pngStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 675 if (pngStruct == nullptr) { 676 fclose(fp); 677 return false; 678 } 679 png_infop pngInfo = png_create_info_struct(pngStruct); 680 if (pngInfo == nullptr) { 681 fclose(fp); 682 png_destroy_write_struct(&pngStruct, nullptr); 683 return false; 684 } 685 png_init_io(pngStruct, fp); 686 auto width = static_cast<uint32_t>(pixelMap->GetWidth()); 687 auto height = static_cast<uint32_t>(pixelMap->GetHeight()); 688 auto data = pixelMap->GetPixels(); 689 auto stride = static_cast<uint32_t>(pixelMap->GetRowBytes()); 690 // set png header 691 static constexpr int bitmapDepth = 8; 692 png_set_IHDR(pngStruct, pngInfo, width, height, bitmapDepth, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, 693 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 694 png_set_packing(pngStruct); // set packing info 695 png_write_info(pngStruct, pngInfo); // write to header 696 for (uint32_t column = 0; column < height; column++) { 697 png_write_row(pngStruct, data + (column * stride)); 698 } 699 // free/close 700 png_write_end(pngStruct, pngInfo); 701 png_destroy_write_struct(&pngStruct, &pngInfo); 702 (void)fclose(fp); 703 return true; 704 } 705 ConnectToSysAbility(ApiCallErr & error)706 bool SysUiController::ConnectToSysAbility(ApiCallErr &error) 707 { 708 if (connected_) { 709 return true; 710 } 711 mutex mtx; 712 unique_lock<mutex> uLock(mtx); 713 std::shared_ptr<condition_variable> condition = make_shared<condition_variable>(); 714 auto onConnectCallback = [condition]() { 715 LOG_I("Success connect to AccessibilityUITestAbility"); 716 condition->notify_all(); 717 }; 718 auto onDisConnectCallback = [this]() { this->connected_ = false; }; 719 if (g_monitorInstance_ == nullptr) { 720 g_monitorInstance_ = make_shared<UiEventMonitor>(); 721 } 722 g_monitorInstance_->SetOnAbilityConnectCallback(onConnectCallback); 723 g_monitorInstance_->SetOnAbilityDisConnectCallback(onDisConnectCallback); 724 auto ability = AccessibilityUITestAbility::GetInstance(); 725 if (ability->RegisterAbilityListener(g_monitorInstance_) != RET_OK) { 726 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS, REGISTER_LISTENER_FAILED"); 727 return false; 728 } 729 auto ret = ability->Connect(); 730 LOG_I("Connect to AAMS, result: %{public}d", ret); 731 if (ret != RET_OK) { 732 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS"); 733 if (ret == RET_ERR_CONNECTION_EXIST) { 734 error.message_ += ", RET_ERR_CONNECTION_EXIST"; 735 } else { 736 error.message_ += ", RET_ERR_AAMS"; 737 } 738 return false; 739 } 740 const auto timeout = chrono::milliseconds(3000); 741 if (condition->wait_for(uLock, timeout) == cv_status::timeout) { 742 LOG_E("Wait connection to AccessibilityUITestAbility timed out"); 743 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS, RET_TIMEOUT"); 744 return false; 745 } 746 connected_ = true; 747 return true; 748 } 749 RegisterUiEventListener(std::shared_ptr<UiEventListener> listener) const750 void SysUiController::RegisterUiEventListener(std::shared_ptr<UiEventListener> listener) const 751 { 752 g_monitorInstance_->RegisterUiEventListener(listener); 753 } 754 WaitForUiSteady(uint32_t idleThresholdMs,uint32_t timeoutMs) const755 bool SysUiController::WaitForUiSteady(uint32_t idleThresholdMs, uint32_t timeoutMs) const 756 { 757 return g_monitorInstance_->WaitEventIdle(idleThresholdMs, timeoutMs); 758 } 759 DisConnectFromSysAbility()760 void SysUiController::DisConnectFromSysAbility() 761 { 762 if (!connected_ || g_monitorInstance_ == nullptr) { 763 return; 764 } 765 connected_ = false; 766 mutex mtx; 767 unique_lock<mutex> uLock(mtx); 768 condition_variable condition; 769 auto onDisConnectCallback = [&condition]() { 770 LOG_I("Success disconnect from AccessibilityUITestAbility"); 771 condition.notify_all(); 772 }; 773 g_monitorInstance_->SetOnAbilityDisConnectCallback(onDisConnectCallback); 774 auto ability = AccessibilityUITestAbility::GetInstance(); 775 LOG_I("Start disconnect from AccessibilityUITestAbility"); 776 if (ability->Disconnect() != RET_OK) { 777 LOG_E("Failed to disconnect from AccessibilityUITestAbility"); 778 return; 779 } 780 const auto timeout = chrono::milliseconds(200); 781 if (condition.wait_for(uLock, timeout) == cv_status::timeout) { 782 LOG_E("Wait disconnection from AccessibilityUITestAbility timed out"); 783 return; 784 } 785 } 786 SetDisplayRotation(DisplayRotation rotation) const787 void SysUiController::SetDisplayRotation(DisplayRotation rotation) const 788 { 789 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 790 if (display == nullptr) { 791 LOG_E("DisplayManager init fail"); 792 return; 793 } 794 auto screenId = display->GetScreenId(); 795 ScreenManager &screenMgr = ScreenManager::GetInstance(); 796 DCHECK(screenMgr); 797 bool isLocked = false; 798 screenMgr.IsScreenRotationLocked(isLocked); 799 if (isLocked) { 800 screenMgr.SetScreenRotationLocked(false); 801 } 802 auto screen = screenMgr.GetScreenById(screenId); 803 if (screen == nullptr) { 804 LOG_E("ScreenManager init fail"); 805 return; 806 } 807 switch (rotation) { 808 case ROTATION_0 : 809 screen->SetOrientation(Orientation::VERTICAL); 810 break; 811 case ROTATION_90 : 812 screen->SetOrientation(Orientation::HORIZONTAL); 813 break; 814 case ROTATION_180 : 815 screen->SetOrientation(Orientation::REVERSE_VERTICAL); 816 break; 817 case ROTATION_270 : 818 screen->SetOrientation(Orientation::REVERSE_HORIZONTAL); 819 break; 820 default : 821 break; 822 } 823 } 824 GetDisplayRotation() const825 DisplayRotation SysUiController::GetDisplayRotation() const 826 { 827 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 828 if (display == nullptr) { 829 LOG_E("DisplayManager init fail"); 830 return DisplayRotation::ROTATION_0; 831 } 832 auto rotation = (DisplayRotation)display->GetRotation(); 833 return rotation; 834 } 835 SetDisplayRotationEnabled(bool enabled) const836 void SysUiController::SetDisplayRotationEnabled(bool enabled) const 837 { 838 ScreenManager &screenMgr = ScreenManager::GetInstance(); 839 DCHECK(screenMgr); 840 screenMgr.SetScreenRotationLocked(!enabled); 841 } 842 GetDisplaySize() const843 Point SysUiController::GetDisplaySize() const 844 { 845 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 846 if (display == nullptr) { 847 LOG_E("DisplayManager init fail"); 848 return {0, 0}; 849 } 850 auto width = display->GetWidth(); 851 auto height = display->GetHeight(); 852 LOG_D("GetDisplaysize, width: %{public}d, height: %{public}d", width, height); 853 Point result(width, height); 854 return result; 855 } 856 GetDisplayDensity() const857 Point SysUiController::GetDisplayDensity() const 858 { 859 auto display = DisplayManager::GetInstance().GetDefaultDisplay(); 860 if (display == nullptr) { 861 LOG_E("DisplayManager init fail"); 862 return {0, 0}; 863 } 864 auto rate = display->GetVirtualPixelRatio(); 865 Point displaySize = GetDisplaySize(); 866 Point result(displaySize.px_ * rate, displaySize.py_ * rate); 867 return result; 868 } 869 IsScreenOn() const870 bool SysUiController::IsScreenOn() const 871 { 872 DisplayManager &displayMgr = DisplayManager::GetInstance(); 873 DCHECK(displayMgr); 874 auto displayId = displayMgr.GetDefaultDisplayId(); 875 auto state = displayMgr.GetDisplayState(displayId); 876 return (state != DisplayState::OFF); 877 } 878 879 class OnSaLoadCallback : public SystemAbilityLoadCallbackStub { 880 public: OnSaLoadCallback(mutex & mutex)881 explicit OnSaLoadCallback(mutex &mutex): mutex_(mutex) {}; ~OnSaLoadCallback()882 ~OnSaLoadCallback() {}; OnLoadSystemAbilitySuccess(int32_t systemAbilityId,const sptr<IRemoteObject> & remoteObject)883 void OnLoadSystemAbilitySuccess(int32_t systemAbilityId, const sptr<IRemoteObject>& remoteObject) override 884 { 885 if (systemAbilityId == OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID) { 886 remoteObject_ = remoteObject; 887 mutex_.unlock(); 888 } 889 } OnLoadSystemAbilityFail(int32_t systemAbilityId)890 void OnLoadSystemAbilityFail(int32_t systemAbilityId) override 891 { 892 if (systemAbilityId == OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID) { 893 mutex_.unlock(); 894 } 895 } 896 GetSaObject()897 sptr<IRemoteObject> GetSaObject() 898 { 899 return remoteObject_; 900 } 901 902 private: 903 mutex &mutex_; 904 sptr<IRemoteObject> remoteObject_ = nullptr; 905 }; 906 CreateHidumperCmd(const std::string & windowId,vector<u16string> & result)907 static void CreateHidumperCmd(const std::string &windowId, vector<u16string> &result) 908 { 909 result.emplace_back(u"hidumper"); 910 result.emplace_back(u"-s"); 911 result.emplace_back(u"WindowManagerService"); 912 result.emplace_back(u"-a"); 913 auto winIdInUtf16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(windowId); 914 auto arg = u16string(u"-w ").append(winIdInUtf16).append(u" -default -lastpage"); 915 result.emplace_back(move(arg)); 916 } 917 GetHidumperInfo(std::string windowId,char ** buf,size_t & len)918 void SysUiController::GetHidumperInfo(std::string windowId, char **buf, size_t &len) 919 { 920 #ifdef HIDUMPER_ENABLED 921 auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 922 // wati SA start 923 constexpr auto delayMs = 2000; 924 this_thread::sleep_for(chrono::milliseconds(delayMs)); 925 if (sam == nullptr) { 926 LOG_E("Get samgr failed"); 927 return; 928 } 929 auto remoteObject = sam->CheckSystemAbility(OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID); 930 if (remoteObject == nullptr) { 931 mutex lock; 932 lock.lock(); 933 sptr<OnSaLoadCallback> loadCallback = new OnSaLoadCallback(lock); 934 if (sam->LoadSystemAbility(OHOS::DFX_HI_DUMPER_SERVICE_ABILITY_ID, loadCallback) != ERR_OK) { 935 LOG_E("Schedule LoadSystemAbility failed"); 936 lock.unlock(); 937 return; 938 } 939 LOG_E("Schedule LoadSystemAbility succeed"); 940 lock.unlock(); 941 remoteObject = loadCallback->GetSaObject(); 942 LOG_E("LoadSystemAbility callbacked, result = %{public}s", remoteObject == nullptr ? "FAIL" : "SUCCESS"); 943 } 944 if (remoteObject == nullptr) { 945 LOG_E("remoteObject is null"); 946 return; 947 } 948 // run dump command 949 sptr<IDumpBroker> client = iface_cast<IDumpBroker>(remoteObject); 950 if (client == nullptr) { 951 LOG_E("IDumpBroker converts failed"); 952 return; 953 } 954 auto fd = memfd_create("dummy_file", 2); 955 ftruncate(fd, 0); 956 vector<u16string> args; 957 CreateHidumperCmd(windowId, args); 958 client->Request(args, fd); 959 auto size = lseek(fd, 0, SEEK_END); 960 char *tempBuf = new char[size + 1]; 961 lseek(fd, 0, SEEK_SET); 962 read(fd, tempBuf, size); 963 *buf = tempBuf; 964 len = size; 965 close(fd); 966 #else 967 *buf = nullptr; 968 len = 0; 969 #endif 970 } 971 } // namespace OHOS::uitest