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 std::string EventData::defaultDir = InputEventCallback::DEFAULT_DIR; 56 SpecialKeyMapExistKey(int32_t keyCode,TouchOpt & touchop)57 bool SpecialKeyMapExistKey(int32_t keyCode, TouchOpt &touchop) 58 { 59 auto iter = SPECIAL_KET_MAP.find(keyCode); 60 if (iter!=SPECIAL_KET_MAP.end()) { 61 touchop = iter->second; 62 return true; 63 } 64 return false; 65 } 66 67 // KEY订阅模板函数 KeyEventSubscribeTemplate(SubscribeKeyevent & subscribeKeyevent)68 void InputEventCallback::KeyEventSubscribeTemplate(SubscribeKeyevent& subscribeKeyevent) 69 { 70 std::set<int32_t> preKeys; 71 std::shared_ptr<MMI::KeyOption> keyOption = std::make_shared<MMI::KeyOption>(); 72 keyOption->SetPreKeys(preKeys); 73 keyOption->SetFinalKey(subscribeKeyevent.keyCode); 74 keyOption->SetFinalKeyDown(subscribeKeyevent.isDown); 75 keyOption->SetFinalKeyDownDuration(KEY_DOWN_DURATION); 76 subscribeKeyevent.subId = MMI::InputManager::GetInstance()->SubscribeKeyEvent(keyOption, 77 [this](std::shared_ptr<MMI::KeyEvent> keyEvent) { 78 OnInputEvent(keyEvent); 79 }); 80 } 81 82 // key订阅 SubscribeMonitorInit()83 void InputEventCallback::SubscribeMonitorInit() 84 { 85 for (size_t i = 0; i < NEED_SUBSCRIBE_KEY.size(); i++) { 86 KeyEventSubscribeTemplate(NEED_SUBSCRIBE_KEY[i]); 87 } 88 } 89 // key取消订阅 SubscribeMonitorCancel()90 void InputEventCallback::SubscribeMonitorCancel() 91 { 92 MMI::InputManager* inputManager = MMI::InputManager::GetInstance(); 93 if (inputManager == nullptr) { 94 return; 95 } 96 for (size_t i = 0; i < NEED_SUBSCRIBE_KEY.size(); i++) { 97 if (NEED_SUBSCRIBE_KEY[i].subId >= 0) { 98 inputManager->UnsubscribeKeyEvent(NEED_SUBSCRIBE_KEY[i].subId); 99 } 100 } 101 } 102 ReadEventLine()103 void EventData::ReadEventLine() 104 { 105 std::ifstream inFile(defaultDir + "/" + "record.csv"); 106 std::string line; 107 if (inFile.is_open()) { 108 while (std::getline(inFile, line)) { 109 std::cout << line << std::endl; 110 } 111 inFile.close(); 112 } 113 } 114 115 // KEY_ACTION OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const116 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const 117 { 118 if (dealSpecialKey(keyEvent)) { 119 return; 120 } 121 touchTime = GetCurrentMillisecond(); 122 auto item = keyEvent->GetKeyItem(keyEvent->GetKeyCode()); 123 if (!item) { 124 std::cout << "GetPointerItem Fail" << std::endl; 125 } 126 KeyEventInfo info; 127 info.SetActionTime(keyEvent->GetActionTime()); 128 info.SetKeyCode(keyEvent->GetKeyCode()); 129 if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { 130 // 三键以上的同时按键无效 131 if (keyeventTracker_.GetInfos().size() >= KeyeventTracker::MAX_COMBINATION_SIZE) { 132 LOG_I("More than three keys are invalid at the same time"); 133 std::cout << "More than three keys are invalid at the same time" << std::endl; 134 return; 135 } 136 if (KeyeventTracker::IsCombinationKey(info.GetKeyCode())) { 137 keyeventTracker_.SetNeedRecord(true); 138 keyeventTracker_.AddDownKeyEvent(info); 139 } else { 140 keyeventTracker_.SetNeedRecord(false); 141 KeyeventTracker snapshootKeyTracker = keyeventTracker_.GetSnapshootKey(info); 142 // cout打印 + record.csv保存 143 snapshootKeyTracker.WriteSingleData(info, cout_lock); 144 auto json = snapshootKeyTracker.WriteSingleData(info, outFile, csv_lock); 145 DoAbcCallBack(json); 146 } 147 } else if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_UP) { 148 if (!KeyeventTracker::IsCombinationKey(info.GetKeyCode())) { 149 return; 150 } 151 if (keyeventTracker_.IsNeedRecord()) { 152 keyeventTracker_.SetNeedRecord(false); 153 KeyeventTracker snapshootKeyTracker = keyeventTracker_.GetSnapshootKey(info); 154 // cout打印 + record.csv保存json 155 snapshootKeyTracker.WriteCombinationData(cout_lock); 156 auto json = snapshootKeyTracker.WriteCombinationData(outFile, csv_lock); 157 DoAbcCallBack(json); 158 } 159 keyeventTracker_.AddUpKeyEvent(info); 160 } 161 } 162 DoAbcCallBack(nlohmann::json jsonData) const163 void InputEventCallback::DoAbcCallBack(nlohmann::json jsonData) const 164 { 165 if (abcCallBack != nullptr) { 166 auto data = nlohmann::json(); 167 data["code"] = MessageStage::FindWidgetsEnd; 168 data["data"] = jsonData; 169 abcCallBack(data); 170 } 171 } 172 dealSpecialKey(std::shared_ptr<MMI::KeyEvent> keyEvent) const173 bool InputEventCallback::dealSpecialKey(std::shared_ptr<MMI::KeyEvent> keyEvent) const 174 { 175 TouchOpt touchOpt; 176 if (SpecialKeyMapExistKey(keyEvent->GetKeyCode(), touchOpt)) { 177 if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) { 178 return true; 179 } else if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_UP) { 180 PointerInfo& info = pointerTracker_.GetSnapshootPointerInfo(); 181 info.SetTouchOpt(touchOpt); 182 findWidgetsAllow_ = true; 183 widgetsCon.notify_all(); 184 pointerTracker_.SetLastClickInTracker(false); 185 return true; 186 } 187 } 188 return false; 189 } 190 191 // AXIS_ACTION OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const192 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const { 193 } 194 TimerReprintClickFunction()195 void InputEventCallback::TimerReprintClickFunction() 196 { 197 while (g_uiRecordRun) { 198 std::unique_lock <std::mutex> clickLck(g_clickMut); 199 while (!isLastClick_) { 200 clickCon.wait(clickLck); 201 } 202 std::this_thread::sleep_for( 203 std::chrono::milliseconds( 204 static_cast<int>(PointerTracker::INTERVAL_THRESHOLD * VelocityTracker::TIME_INDEX))); 205 if (isLastClick_) { 206 isLastClick_ = false; 207 pointerTracker_.SetLastClickInTracker(false); 208 findWidgetsAllow_ = true; 209 widgetsCon.notify_all(); 210 } 211 } 212 } 213 TimerTouchCheckFunction()214 void InputEventCallback::TimerTouchCheckFunction() 215 { 216 while (g_uiRecordRun) { 217 unique_lock<mutex> lock(timerMut); 218 timerCon.wait_for(lock, std::chrono::milliseconds(TIMEINTERVAL), [this] {return stopFlag;}); 219 int currentTime = GetCurrentMillisecond(); 220 int diff = currentTime - touchTime; 221 if (diff >= TIMEINTERVAL) { 222 std::cout << "No operation detected for 5 seconds, press ctrl + c to save this file?" << std::endl; 223 } 224 } 225 } 226 FindWidgetsandWriteData()227 void InputEventCallback::FindWidgetsandWriteData() 228 { 229 while (g_uiRecordRun) { 230 std::unique_lock<std::mutex> widgetsLck(widgetsMut); 231 while (!findWidgetsAllow_) { 232 widgetsCon.wait(widgetsLck); 233 } 234 if (!g_uiRecordRun) { 235 return; 236 } 237 if (abcCallBack != nullptr) { 238 auto data = nlohmann::json(); 239 data["code"] = MessageStage::StartFindWidgets; 240 abcCallBack(data); 241 } 242 std::this_thread::sleep_for(std::chrono::milliseconds(VelocityTracker::TIME_INDEX)); // 确保界面已更新 243 ApiCallErr err(NO_ERROR); 244 auto layout = nlohmann::json(); 245 driver.DumpUiHierarchy(layout, false, false, 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)->GetAttrVec(); 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)->GetAttrVec(); 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)->GetAttrVec(); 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