1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #include <chrono> 16 #include <thread> 17 #include "external_calls.h" 18 #include "ui_record.h" 19 20 namespace OHOS::uitest { 21 using namespace std; 22 using namespace std::chrono; 23 using nlohmann::json; 24 25 static bool g_uiRecordRun = false; 26 int g_callBackId = -1; 27 int64_t g_downTime = 0; 28 static std::shared_ptr<InputEventCallback> g_uiCallBackInstance = nullptr; 29 const std::map <int32_t, TouchOpt> SPECIAL_KET_MAP = { 30 {MMI::KeyEvent::KEYCODE_BACK, TouchOpt::OP_RETURN}, 31 {MMI::KeyEvent::KEYCODE_HOME, TouchOpt::OP_HOME}, 32 }; 33 34 static std::vector<SubscribeKeyevent> NEED_SUBSCRIBE_KEY = { 35 {MMI::KeyEvent::KEYCODE_POWER, true, 0}, // 电源键按下订阅 36 {MMI::KeyEvent::KEYCODE_POWER, false, 0}, // 电源键抬起订阅 37 {MMI::KeyEvent::KEYCODE_VOLUME_UP, true, 0}, // 音量UP键按下订阅 38 {MMI::KeyEvent::KEYCODE_VOLUME_DOWN, true, 0}, // 音量DOWN键按下订阅 39 {MMI::KeyEvent::KEYCODE_ESCAPE, true, 0}, // esc键按下订阅 40 {MMI::KeyEvent::KEYCODE_ESCAPE, false, 0}, // esc键抬起订阅 41 {MMI::KeyEvent::KEYCODE_F1, true, 0}, // f1键按下订阅 42 {MMI::KeyEvent::KEYCODE_F1, false, 0}, // f1键抬起订阅 43 {MMI::KeyEvent::KEYCODE_ALT_LEFT, true, 0}, // alt-left键按下订阅 44 {MMI::KeyEvent::KEYCODE_ALT_LEFT, false, 0}, // alt-left键抬起订阅 45 {MMI::KeyEvent::KEYCODE_ALT_RIGHT, true, 0}, // alt-right键按下订阅 46 {MMI::KeyEvent::KEYCODE_ALT_RIGHT, false, 0}, // alt-right键抬起订阅 47 {MMI::KeyEvent::KEYCODE_FN, true, 0}, // fn键按下订阅 48 {MMI::KeyEvent::KEYCODE_FN, false, 0}, // fn键抬起订阅 49 }; 50 enum MessageStage : uint8_t { 51 StartUp = 0, StartEnd, StartFindWidgets, FindWidgetsEnd 52 }; 53 54 inline const std::string InputEventCallback::DEFAULT_DIR = "/data/local/tmp"; 55 SpecialKeyMapExistKey(int32_t keyCode,TouchOpt & touchop)56 bool SpecialKeyMapExistKey(int32_t keyCode, TouchOpt &touchop) 57 { 58 auto iter = SPECIAL_KET_MAP.find(keyCode); 59 if (iter!=SPECIAL_KET_MAP.end()) { 60 touchop = iter->second; 61 return true; 62 } 63 return false; 64 } 65 66 // KEY订阅模板函数 KeyEventSubscribeTemplate(SubscribeKeyevent & subscribeKeyevent)67 void InputEventCallback::KeyEventSubscribeTemplate(SubscribeKeyevent& subscribeKeyevent) 68 { 69 std::set<int32_t> preKeys; 70 std::shared_ptr<MMI::KeyOption> keyOption = std::make_shared<MMI::KeyOption>(); 71 keyOption->SetPreKeys(preKeys); 72 keyOption->SetFinalKey(subscribeKeyevent.keyCode); 73 keyOption->SetFinalKeyDown(subscribeKeyevent.isDown); 74 keyOption->SetFinalKeyDownDuration(KEY_DOWN_DURATION); 75 subscribeKeyevent.subId = MMI::InputManager::GetInstance()->SubscribeKeyEvent(keyOption, 76 [this](std::shared_ptr<MMI::KeyEvent> keyEvent) { 77 OnInputEvent(keyEvent); 78 }); 79 } 80 81 // key订阅 SubscribeMonitorInit()82 void InputEventCallback::SubscribeMonitorInit() 83 { 84 for (size_t i = 0; i < NEED_SUBSCRIBE_KEY.size(); i++) { 85 KeyEventSubscribeTemplate(NEED_SUBSCRIBE_KEY[i]); 86 } 87 } 88 // key取消订阅 SubscribeMonitorCancel()89 void InputEventCallback::SubscribeMonitorCancel() 90 { 91 MMI::InputManager* inputManager = MMI::InputManager::GetInstance(); 92 if (inputManager == nullptr) { 93 return; 94 } 95 for (size_t i = 0; i < NEED_SUBSCRIBE_KEY.size(); i++) { 96 if (NEED_SUBSCRIBE_KEY[i].subId >= 0) { 97 inputManager->UnsubscribeKeyEvent(NEED_SUBSCRIBE_KEY[i].subId); 98 } 99 } 100 } 101 ReadEventLine()102 void EventData::ReadEventLine() 103 { 104 std::ifstream inFile(InputEventCallback::DEFAULT_DIR + "/" + "record.csv"); 105 std::string line; 106 if (inFile.is_open()) { 107 while (std::getline(inFile, line)) { 108 std::cout << line << std::endl; 109 } 110 inFile.close(); 111 } 112 } 113 114 // KEY_ACTION OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const115 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const 116 { 117 if (dealSpecialKey(keyEvent)) { 118 return; 119 } 120 touchTime = GetCurrentMillisecond(); 121 auto item = keyEvent->GetKeyItem(keyEvent->GetKeyCode()); 122 if (!item) { 123 std::cout << "GetPointerItem Fail" << std::endl; 124 } 125 KeyEventInfo info; 126 info.SetActionTime(keyEvent->GetActionTime()); 127 info.SetKeyCode(keyEvent->GetKeyCode()); 128 if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { 129 // 三键以上的同时按键无效 130 if (keyeventTracker_.GetInfos().size() >= KeyeventTracker::MAX_COMBINATION_SIZE) { 131 LOG_I("More than three keys are invalid at the same time"); 132 std::cout << "More than three keys are invalid at the same time" << std::endl; 133 return; 134 } 135 if (KeyeventTracker::IsCombinationKey(info.GetKeyCode())) { 136 keyeventTracker_.SetNeedRecord(true); 137 keyeventTracker_.AddDownKeyEvent(info); 138 } else { 139 keyeventTracker_.SetNeedRecord(false); 140 KeyeventTracker snapshootKeyTracker = keyeventTracker_.GetSnapshootKey(info); 141 // cout打印 + record.csv保存 142 snapshootKeyTracker.WriteSingleData(info, cout_lock); 143 auto json = snapshootKeyTracker.WriteSingleData(info, outFile, csv_lock); 144 DoAbcCallBack(json); 145 } 146 } else if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_UP) { 147 if (!KeyeventTracker::IsCombinationKey(info.GetKeyCode())) { 148 return; 149 } 150 if (keyeventTracker_.IsNeedRecord()) { 151 keyeventTracker_.SetNeedRecord(false); 152 KeyeventTracker snapshootKeyTracker = keyeventTracker_.GetSnapshootKey(info); 153 // cout打印 + record.csv保存json 154 snapshootKeyTracker.WriteCombinationData(cout_lock); 155 auto json = snapshootKeyTracker.WriteCombinationData(outFile, csv_lock); 156 DoAbcCallBack(json); 157 } 158 keyeventTracker_.AddUpKeyEvent(info); 159 } 160 } 161 DoAbcCallBack(nlohmann::json jsonData) const162 void InputEventCallback::DoAbcCallBack(nlohmann::json jsonData) const 163 { 164 if (abcCallBack != nullptr) { 165 auto data = nlohmann::json(); 166 data["code"] = MessageStage::FindWidgetsEnd; 167 data["data"] = jsonData; 168 abcCallBack(data); 169 } 170 } 171 dealSpecialKey(std::shared_ptr<MMI::KeyEvent> keyEvent) const172 bool InputEventCallback::dealSpecialKey(std::shared_ptr<MMI::KeyEvent> keyEvent) const 173 { 174 TouchOpt touchOpt; 175 if (SpecialKeyMapExistKey(keyEvent->GetKeyCode(), touchOpt)) { 176 if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { 177 return true; 178 } else if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_UP) { 179 PointerInfo& info = pointerTracker_.GetSnapshootPointerInfo(); 180 info.SetTouchOpt(touchOpt); 181 findWidgetsAllow_ = true; 182 widgetsCon.notify_all(); 183 pointerTracker_.SetLastClickInTracker(false); 184 return true; 185 } 186 } 187 return false; 188 } 189 190 // AXIS_ACTION OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const191 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const { 192 } 193 TimerReprintClickFunction()194 void InputEventCallback::TimerReprintClickFunction() 195 { 196 while (g_uiRecordRun) { 197 std::unique_lock <std::mutex> clickLck(g_clickMut); 198 while (!isLastClick_) { 199 clickCon.wait(clickLck); 200 } 201 std::this_thread::sleep_for( 202 std::chrono::milliseconds( 203 static_cast<int>(PointerTracker::INTERVAL_THRESHOLD * VelocityTracker::TIME_INDEX))); 204 if (isLastClick_) { 205 isLastClick_ = false; 206 pointerTracker_.SetLastClickInTracker(false); 207 findWidgetsAllow_ = true; 208 widgetsCon.notify_all(); 209 } 210 } 211 } 212 TimerTouchCheckFunction()213 void InputEventCallback::TimerTouchCheckFunction() 214 { 215 while (g_uiRecordRun) { 216 unique_lock<mutex> lock(timerMut); 217 timerCon.wait_for(lock, std::chrono::milliseconds(TIMEINTERVAL), [this] {return stopFlag;}); 218 int currentTime = GetCurrentMillisecond(); 219 int diff = currentTime - touchTime; 220 if (diff >= TIMEINTERVAL) { 221 std::cout << "No operation detected for 5 seconds, press ctrl + c to save this file?" << std::endl; 222 } 223 } 224 } 225 FindWidgetsandWriteData()226 void InputEventCallback::FindWidgetsandWriteData() 227 { 228 while (g_uiRecordRun) { 229 std::unique_lock<std::mutex> widgetsLck(widgetsMut); 230 while (!findWidgetsAllow_) { 231 widgetsCon.wait(widgetsLck); 232 } 233 if (!g_uiRecordRun) { 234 return; 235 } 236 if (abcCallBack != nullptr) { 237 auto data = nlohmann::json(); 238 data["code"] = MessageStage::StartFindWidgets; 239 abcCallBack(data); 240 } 241 std::this_thread::sleep_for(std::chrono::milliseconds(VelocityTracker::TIME_INDEX)); // 确保界面已更新 242 ApiCallErr err(NO_ERROR); 243 auto layout = nlohmann::json(); 244 DumpOption option; 245 driver.DumpUiHierarchy(layout, option, err); 246 selector.SetWantMulti(true); 247 driver.FindWidgets(selector, rev, err, true); 248 PointerInfo& info = pointerTracker_.GetSnapshootPointerInfo(); 249 pointerTracker_.WriteData(info, cout_lock); 250 auto json = pointerTracker_.WriteData(info, outFile, csv_lock); 251 if (abcCallBack != nullptr) { 252 auto data = nlohmann::json(); 253 data["code"] = MessageStage::FindWidgetsEnd; 254 data["data"] = json; 255 data["layout"] = layout; 256 abcCallBack(data); 257 } 258 findWidgetsAllow_ = false; 259 widgetsCon.notify_all(); 260 } 261 } 262 WritePointerInfo() const263 void InputEventCallback::WritePointerInfo() const 264 { 265 if (pointerTracker_.IsNeedWrite()) { 266 PointerInfo info = pointerTracker_.GetSnapshootPointerInfo(); 267 if (info.GetTouchOpt() != OP_CLICK) { 268 isLastClick_ = false; 269 findWidgetsAllow_ = true; 270 widgetsCon.notify_all(); 271 } 272 if (info.GetTouchOpt() == OP_CLICK) { 273 isLastClick_ = true; 274 clickCon.notify_all(); 275 } 276 pointerTracker_.SetNeedWrite(false); 277 } 278 } 279 OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const280 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const 281 { 282 MMI::PointerEvent::PointerItem item; 283 bool result = pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), item); 284 if (!result) { 285 std::cout << "GetPointerItem Fail" << std::endl; 286 } 287 touchTime = GetCurrentMillisecond(); 288 TouchEventInfo touchEvent {}; 289 if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_DOWN) { 290 g_downTime = pointerEvent->GetActionTime(); 291 } 292 touchEvent.actionTime = pointerEvent->GetActionTime(); 293 touchEvent.downTime = item.GetDownTime() == 0 ? g_downTime : item.GetDownTime(); 294 touchEvent.x = item.GetDisplayX(); 295 touchEvent.y = item.GetDisplayY(); 296 touchEvent.wx = item.GetWindowX(); 297 touchEvent.wy = item.GetWindowY(); 298 std::chrono::duration<double> duration = touchEvent.GetActionTimeStamp() - touchEvent.GetDownTimeStamp(); 299 touchEvent.durationSeconds = duration.count(); 300 if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_DOWN) { 301 std::unique_lock<std::mutex> widgetsLck(widgetsMut); 302 while (findWidgetsAllow_) { 303 widgetsCon.wait(widgetsLck); 304 } 305 if (recordMode != "point") { 306 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y); 307 } 308 pointerTracker_.HandleDownEvent(touchEvent); 309 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_MOVE) { 310 pointerTracker_.HandleMoveEvent(touchEvent); 311 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_PULL_MOVE) { 312 pointerTracker_.HandleMoveEvent(touchEvent, OP_DRAG); 313 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_UP) { 314 if (recordMode != "point") { 315 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y); 316 } 317 pointerTracker_.HandleUpEvent(touchEvent); 318 WritePointerInfo(); 319 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_PULL_UP) { 320 if (recordMode != "point") { 321 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y); 322 } 323 pointerTracker_.HandleUpEvent(touchEvent, OP_DRAG); 324 WritePointerInfo(); 325 } 326 } 327 InitReportFolder()328 bool InputEventCallback::InitReportFolder() 329 { 330 if (opendir(DEFAULT_DIR.c_str()) == nullptr) { 331 int ret = mkdir(DEFAULT_DIR.c_str(), S_IROTH | S_IRWXU | S_IRWXG); 332 if (ret != 0) { 333 std::cerr << "failed to create dir: " << DEFAULT_DIR << std::endl; 334 return false; 335 } 336 } 337 return true; 338 } 339 InitEventRecordFile()340 bool InputEventCallback::InitEventRecordFile() 341 { 342 if (!InitReportFolder()) { 343 return false; 344 } 345 filePath = DEFAULT_DIR + "/" + "record.csv"; 346 outFile.open(filePath, std::ios_base::out | std::ios_base::trunc); 347 if (!outFile) { 348 std::cerr << "Failed to create csv file at:" << filePath << std::endl; 349 return false; 350 } 351 std::cout << "The result will be written in csv file at location: " << filePath << std::endl; 352 return true; 353 } RecordInitEnv(const std::string & modeOpt)354 void InputEventCallback::RecordInitEnv(const std::string &modeOpt) 355 { 356 recordMode = modeOpt; 357 ApiCallErr err(NO_ERROR); 358 selector.SetWantMulti(true); 359 driver.FindWidgets(selector, rev, err, true); 360 auto screenSize = driver.GetDisplaySize(err); 361 Rect windowBounds = Rect(0, screenSize.px_, 0, screenSize.py_); 362 std::cout<< "windowBounds : (" << windowBounds.left_ << "," 363 << windowBounds.top_ << "," << windowBounds.right_ << "," 364 << windowBounds.bottom_ << ")" << std::endl; 365 pointerTracker_.SetWindow(windowBounds); 366 std::vector<std::string> names = GetFrontAbility(); 367 std::cout << "Current ForAbility :" << names[ZERO] << ", " << names[ONE] << std::endl; 368 if (outFile.is_open()) { 369 outFile << "windowBounds" << ','; 370 outFile << windowBounds.left_ << ','; 371 outFile << windowBounds.top_ << ','; 372 outFile << windowBounds.right_ << ','; 373 outFile << windowBounds.bottom_ << ','; 374 outFile << "0,0,0,0,,,,,,,"; 375 outFile << names[ZERO] << ','; 376 outFile << names[ONE] << ',' << std::endl; 377 } 378 } 379 UiDriverRecordStart(std::string modeOpt)380 int32_t UiDriverRecordStart(std::string modeOpt) 381 { 382 g_uiCallBackInstance = std::make_shared<InputEventCallback>(); 383 return UiDriverRecordStartTemplate(modeOpt); 384 } 385 UiDriverRecordStart(std::function<void (nlohmann::json)> handler,std::string modeOpt)386 int32_t UiDriverRecordStart(std::function<void(nlohmann::json)> handler, std::string modeOpt) 387 { 388 g_uiCallBackInstance = std::make_shared<InputEventCallback>(); 389 g_uiCallBackInstance->SetAbcCallBack(handler); 390 return UiDriverRecordStartTemplate(modeOpt); 391 } 392 UiDriverRecordStartTemplate(std::string modeOpt)393 int32_t UiDriverRecordStartTemplate(std::string modeOpt) 394 { 395 if (g_uiCallBackInstance == nullptr) { 396 std::cout << "nullptr" << std::endl; 397 return OHOS::ERR_INVALID_VALUE; 398 } 399 auto abcCallBack = g_uiCallBackInstance->GetAbcCallBack(); 400 if (abcCallBack != nullptr) { 401 auto data = nlohmann::json(); 402 data["code"] = MessageStage::StartUp; 403 abcCallBack(data); 404 } 405 g_uiCallBackInstance->RecordInitEnv(modeOpt); 406 if (!g_uiCallBackInstance->InitEventRecordFile()) { 407 return OHOS::ERR_INVALID_VALUE; 408 } 409 // 按键订阅 410 g_uiCallBackInstance->SubscribeMonitorInit(); 411 g_callBackId = MMI::InputManager::GetInstance()->AddMonitor(g_uiCallBackInstance); 412 if (g_callBackId == -1) { 413 std::cout << "Startup Failed!" << std::endl; 414 return OHOS::ERR_INVALID_VALUE; 415 } 416 g_uiRecordRun = true; 417 // 补充click打印线程 418 std::thread clickThread(&InputEventCallback::TimerReprintClickFunction, g_uiCallBackInstance); 419 // touch计时线程 420 std::thread toughTimerThread(&InputEventCallback::TimerTouchCheckFunction, g_uiCallBackInstance); 421 // widget&data 线程 422 std::thread dataThread(&InputEventCallback::FindWidgetsandWriteData, g_uiCallBackInstance); 423 std::cout << "Started Recording Successfully..." << std::endl; 424 if (abcCallBack != nullptr) { 425 auto data = nlohmann::json(); 426 data["code"] = MessageStage::StartEnd; 427 abcCallBack(data); 428 } 429 clickThread.join(); 430 toughTimerThread.join(); 431 dataThread.join(); 432 return OHOS::ERR_OK; 433 } 434 UiDriverRecordStop()435 void UiDriverRecordStop() 436 { 437 g_uiRecordRun = false; 438 if (g_uiCallBackInstance != nullptr) { 439 g_uiCallBackInstance->isLastClick_ = true; 440 g_uiCallBackInstance->findWidgetsAllow_ = true; 441 g_uiCallBackInstance->stopFlag = true; 442 g_uiCallBackInstance->widgetsCon.notify_all(); 443 g_uiCallBackInstance->clickCon.notify_all(); 444 g_uiCallBackInstance->timerCon.notify_all(); 445 g_uiCallBackInstance->SubscribeMonitorCancel(); 446 MMI::InputManager::GetInstance()->RemoveMonitor(g_callBackId); 447 g_callBackId = -1; 448 g_uiCallBackInstance = nullptr; 449 } 450 } 451 } // namespace OHOS::uitest