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/layout"; 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.GetLayoutJson(layout); 246 driver.FindWidgets(selector, rev, err, true); 247 PointerInfo& info = pointerTracker_.GetSnapshootPointerInfo(); 248 pointerTracker_.WriteData(info, cout_lock); 249 auto json = pointerTracker_.WriteData(info, outFile, csv_lock); 250 if (abcCallBack != nullptr) { 251 auto data = nlohmann::json(); 252 data["code"] = MessageStage::FindWidgetsEnd; 253 data["data"] = json; 254 data["layout"] = layout; 255 abcCallBack(data); 256 } 257 findWidgetsAllow_ = false; 258 widgetsCon.notify_all(); 259 } 260 } 261 WritePointerInfo() const262 void InputEventCallback::WritePointerInfo() const 263 { 264 if (pointerTracker_.IsNeedWrite()) { 265 PointerInfo info = pointerTracker_.GetSnapshootPointerInfo(); 266 if (info.GetTouchOpt() != OP_CLICK) { 267 isLastClick_ = false; 268 findWidgetsAllow_ = true; 269 widgetsCon.notify_all(); 270 } 271 if (info.GetTouchOpt() == OP_CLICK) { 272 isLastClick_ = true; 273 clickCon.notify_all(); 274 } 275 pointerTracker_.SetNeedWrite(false); 276 } 277 } 278 OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const279 void InputEventCallback::OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const 280 { 281 MMI::PointerEvent::PointerItem item; 282 bool result = pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), item); 283 if (!result) { 284 std::cout << "GetPointerItem Fail" << std::endl; 285 } 286 touchTime = GetCurrentMillisecond(); 287 TouchEventInfo touchEvent {}; 288 if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_DOWN) { 289 g_downTime = pointerEvent->GetActionTime(); 290 } 291 touchEvent.actionTime = pointerEvent->GetActionTime(); 292 touchEvent.downTime = item.GetDownTime() == 0 ? g_downTime : item.GetDownTime(); 293 touchEvent.x = item.GetDisplayX(); 294 touchEvent.y = item.GetDisplayY(); 295 touchEvent.wx = item.GetWindowX(); 296 touchEvent.wy = item.GetWindowY(); 297 std::chrono::duration<double> duration = touchEvent.GetActionTimeStamp() - touchEvent.GetDownTimeStamp(); 298 touchEvent.durationSeconds = duration.count(); 299 if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_DOWN) { 300 std::unique_lock<std::mutex> widgetsLck(widgetsMut); 301 while (findWidgetsAllow_) { 302 widgetsCon.wait(widgetsLck); 303 } 304 if (recordMode != "point") { 305 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y).GetAttrMap(); 306 } 307 pointerTracker_.HandleDownEvent(touchEvent); 308 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_MOVE) { 309 pointerTracker_.HandleMoveEvent(touchEvent); 310 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_PULL_MOVE) { 311 pointerTracker_.HandleMoveEvent(touchEvent, OP_DRAG); 312 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_UP) { 313 if (recordMode != "point") { 314 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y).GetAttrMap(); 315 } 316 pointerTracker_.HandleUpEvent(touchEvent); 317 WritePointerInfo(); 318 } else if (pointerEvent->GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_PULL_UP) { 319 if (recordMode != "point") { 320 touchEvent.attributes = FindWidget(driver, touchEvent.x, touchEvent.y).GetAttrMap(); 321 } 322 pointerTracker_.HandleUpEvent(touchEvent, OP_DRAG); 323 WritePointerInfo(); 324 } 325 } 326 InitReportFolder()327 bool InputEventCallback::InitReportFolder() 328 { 329 if (opendir(DEFAULT_DIR.c_str()) == nullptr) { 330 int ret = mkdir(DEFAULT_DIR.c_str(), S_IROTH | S_IRWXU | S_IRWXG); 331 if (ret != 0) { 332 std::cerr << "failed to create dir: " << DEFAULT_DIR << std::endl; 333 return false; 334 } 335 } 336 return true; 337 } 338 InitEventRecordFile()339 bool InputEventCallback::InitEventRecordFile() 340 { 341 if (!InitReportFolder()) { 342 return false; 343 } 344 filePath = DEFAULT_DIR + "/" + "record.csv"; 345 outFile.open(filePath, std::ios_base::out | std::ios_base::trunc); 346 if (!outFile) { 347 std::cerr << "Failed to create csv file at:" << filePath << std::endl; 348 return false; 349 } 350 std::cout << "The result will be written in csv file at location: " << filePath << std::endl; 351 return true; 352 } RecordInitEnv(const std::string & modeOpt)353 void InputEventCallback::RecordInitEnv(const std::string &modeOpt) 354 { 355 recordMode = modeOpt; 356 ApiCallErr err(NO_ERROR); 357 driver.FindWidgets(selector, rev, err, true); 358 auto screenSize = driver.GetDisplaySize(err); 359 Rect windowBounds = Rect(0, screenSize.px_, 0, screenSize.py_); 360 std::cout<< "windowBounds : (" << windowBounds.left_ << "," 361 << windowBounds.top_ << "," << windowBounds.right_ << "," 362 << windowBounds.bottom_ << ")" << std::endl; 363 pointerTracker_.SetWindow(windowBounds); 364 std::vector<std::string> names = GetFrontAbility(); 365 std::cout << "Current ForAbility :" << names[ZERO] << ", " << names[ONE] << std::endl; 366 if (outFile.is_open()) { 367 outFile << "windowBounds" << ','; 368 outFile << windowBounds.left_ << ','; 369 outFile << windowBounds.top_ << ','; 370 outFile << windowBounds.right_ << ','; 371 outFile << windowBounds.bottom_ << ','; 372 outFile << "0,0,0,0,,,,,,,"; 373 outFile << names[ZERO] << ','; 374 outFile << names[ONE] << ',' << std::endl; 375 } 376 } 377 UiDriverRecordStart(std::string modeOpt)378 int32_t UiDriverRecordStart(std::string modeOpt) 379 { 380 g_uiCallBackInstance = std::make_shared<InputEventCallback>(); 381 return UiDriverRecordStartTemplate(modeOpt); 382 } 383 UiDriverRecordStart(std::function<void (nlohmann::json)> handler,std::string modeOpt)384 int32_t UiDriverRecordStart(std::function<void(nlohmann::json)> handler, std::string modeOpt) 385 { 386 g_uiCallBackInstance = std::make_shared<InputEventCallback>(); 387 g_uiCallBackInstance->SetAbcCallBack(handler); 388 return UiDriverRecordStartTemplate(modeOpt); 389 } 390 UiDriverRecordStartTemplate(std::string modeOpt)391 int32_t UiDriverRecordStartTemplate(std::string modeOpt) 392 { 393 if (g_uiCallBackInstance == nullptr) { 394 std::cout << "nullptr" << std::endl; 395 return OHOS::ERR_INVALID_VALUE; 396 } 397 auto abcCallBack = g_uiCallBackInstance->GetAbcCallBack(); 398 if (abcCallBack != nullptr) { 399 auto data = nlohmann::json(); 400 data["code"] = MessageStage::StartUp; 401 abcCallBack(data); 402 } 403 g_uiCallBackInstance->RecordInitEnv(modeOpt); 404 if (!g_uiCallBackInstance->InitEventRecordFile()) { 405 return OHOS::ERR_INVALID_VALUE; 406 } 407 // 按键订阅 408 g_uiCallBackInstance->SubscribeMonitorInit(); 409 g_callBackId = MMI::InputManager::GetInstance()->AddMonitor(g_uiCallBackInstance); 410 if (g_callBackId == -1) { 411 std::cout << "Startup Failed!" << std::endl; 412 return OHOS::ERR_INVALID_VALUE; 413 } 414 g_uiRecordRun = true; 415 // 补充click打印线程 416 std::thread clickThread(&InputEventCallback::TimerReprintClickFunction, g_uiCallBackInstance); 417 // touch计时线程 418 std::thread toughTimerThread(&InputEventCallback::TimerTouchCheckFunction, g_uiCallBackInstance); 419 // widget&data 线程 420 std::thread dataThread(&InputEventCallback::FindWidgetsandWriteData, g_uiCallBackInstance); 421 std::cout << "Started Recording Successfully..." << std::endl; 422 if (abcCallBack != nullptr) { 423 auto data = nlohmann::json(); 424 data["code"] = MessageStage::StartEnd; 425 abcCallBack(data); 426 } 427 clickThread.join(); 428 toughTimerThread.join(); 429 dataThread.join(); 430 return OHOS::ERR_OK; 431 } 432 UiDriverRecordStop()433 void UiDriverRecordStop() 434 { 435 g_uiRecordRun = false; 436 if (g_uiCallBackInstance != nullptr) { 437 g_uiCallBackInstance->isLastClick_ = true; 438 g_uiCallBackInstance->findWidgetsAllow_ = true; 439 g_uiCallBackInstance->stopFlag = true; 440 g_uiCallBackInstance->widgetsCon.notify_all(); 441 g_uiCallBackInstance->clickCon.notify_all(); 442 g_uiCallBackInstance->timerCon.notify_all(); 443 g_uiCallBackInstance->SubscribeMonitorCancel(); 444 MMI::InputManager::GetInstance()->RemoveMonitor(g_callBackId); 445 g_callBackId = -1; 446 g_uiCallBackInstance = nullptr; 447 } 448 } 449 } // namespace OHOS::uitest