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 <chrono> 17 #include <unistd.h> 18 #include <memory> 19 #include <iostream> 20 #include <fstream> 21 #include <getopt.h> 22 #include <dirent.h> 23 #include <sys/stat.h> 24 #include <typeinfo> 25 #include <cstring> 26 #include <vector> 27 #include <functional> 28 #include <atomic> 29 #include <mutex> 30 #include <ctime> 31 #include <condition_variable> 32 #include <cmath> 33 #include <string> 34 #include <vector> 35 #include <cmath> 36 #include <fcntl.h> 37 #include "ipc_transactor.h" 38 #include "system_ui_controller.h" 39 #include "input_manager.h" 40 #include "i_input_event_consumer.h" 41 #include "pointer_event.h" 42 #include "ui_driver.h" 43 #include "ui_record.h" 44 #include "ui_input.h" 45 #include "ui_model.h" 46 #include "extension_executor.h" 47 48 using namespace std; 49 using namespace std::chrono; 50 51 namespace OHOS::uitest { 52 const std::string HELP_MSG = 53 "usage: uitest <command> [options] \n" 54 "help print help messages\n" 55 "screenCap capture the current screen\n" 56 " -p <savePath> specifies the savePath\n" 57 " -d <displayId> specifies the screen\n" 58 "dumpLayout get the current layout information\n" 59 " -p <savePath> specifies the savePath\n" 60 " -i not merge windows and filter nodes\n" 61 " -a include font attributes\n" 62 " -b <bundleName> specifies the bundleName of the target window\n" 63 " -w <windowId> specifies the window id of the target window\n" 64 " -m <true/false> whether merge windows, true means to merge, set it ture when not use this option\n" 65 " -d <displayId> specifies the locate screen of the target window\n" 66 "start-daemon <token> start the test process\n" 67 "uiRecord recording Ui Operations\n" 68 " record wirte location coordinates of events into files\n" 69 " read print file content to the console\n" 70 "uiInput inject Ui simulation operations\n" 71 " help print uiInput usage\n" 72 " dircFling <direction> [velocity] [stepLength] direction ranges from 0,1,2,3 (left, right, up, down)\n" 73 " click/doubleClick/longClick <x> <y> click on the target coordinates\n" 74 " swipe/drag <from_x> <from_y> <to_x> <to_y> [velocity] velocity ranges from 200 to 40000, default 600\n" 75 " fling <from_x> <from_y> <to_x> <to_y> [velocity] [stepLength] velocity ranges from 200 to 40000, default 600\n" 76 " keyEvent <keyID/Back/Home/Power> inject keyEvent\n" 77 " keyEvent <keyID_0> <keyID_1> [keyID_2] keyID_2 default to None \n" 78 " inputText <x> <y> <text> inputText at the target coordinate point\n" 79 " text <text> input text at the location where is already focused\n" 80 "--version print current tool version\n"; 81 82 const std::string VERSION = "5.1.1.2"; 83 struct option g_longoptions[] = { 84 {nullptr, required_argument, nullptr, 'p'}, 85 {nullptr, required_argument, nullptr, 'd'}, 86 {nullptr, no_argument, nullptr, 'i'}, 87 {nullptr, no_argument, nullptr, 'a'}, 88 {nullptr, required_argument, nullptr, 'b'}, 89 {nullptr, required_argument, nullptr, 'w'}, 90 {nullptr, required_argument, nullptr, 'm'}, 91 {nullptr, 0, nullptr, 0}}; 92 /* *Print to the console of this shell process. */ PrintToConsole(string_view message)93 static inline void PrintToConsole(string_view message) 94 { 95 std::cout << message << std::endl; 96 } 97 GetParam(int32_t argc,char * argv[],string_view optstring,string_view usage,map<char,string> & params)98 static int32_t GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, 99 map<char, string> ¶ms) 100 { 101 int opt; 102 while ((opt = getopt_long(argc, argv, optstring.data(), g_longoptions, nullptr)) != -1) { 103 switch (opt) { 104 case '?': 105 PrintToConsole(usage); 106 return EXIT_FAILURE; 107 case 'i': 108 params.insert(pair<char, string>(opt, "true")); 109 break; 110 case 'a': 111 params.insert(pair<char, string>(opt, "true")); 112 break; 113 case 'm': 114 if (strcmp(optarg, "true") && strcmp(optarg, "false")) { 115 PrintToConsole("Invalid params"); 116 PrintToConsole(usage); 117 return EXIT_FAILURE; 118 } 119 params.insert(pair<char, string>(opt, optarg)); 120 break; 121 default: 122 params.insert(pair<char, string>(opt, optarg)); 123 break; 124 } 125 } 126 return EXIT_SUCCESS; 127 } 128 DumpLayoutImpl(const DumpOption & option,bool initController,ApiCallErr & err)129 static void DumpLayoutImpl(const DumpOption &option, bool initController, ApiCallErr &err) 130 { 131 ofstream fout; 132 fout.open(option.savePath_, ios::out | ios::binary); 133 if (!fout) { 134 err = ApiCallErr(ERR_INVALID_INPUT, "Error path:" + string(option.savePath_) + strerror(errno)); 135 return; 136 } 137 if (initController) { 138 UiDriver::RegisterController(make_unique<SysUiController>()); 139 } 140 auto driver = UiDriver(); 141 auto data = nlohmann::json(); 142 driver.DumpUiHierarchy(data, option, err); 143 if (err.code_ != NO_ERROR) { 144 fout.close(); 145 return; 146 } 147 fout << data.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); 148 fout.close(); 149 return; 150 } 151 DumpLayout(int32_t argc,char * argv[])152 static int32_t DumpLayout(int32_t argc, char *argv[]) 153 { 154 DumpOption option; 155 auto ts = to_string(GetCurrentMicroseconds()); 156 auto savePath = "/data/local/tmp/layout_" + ts + ".json"; 157 map<char, string> params; 158 if (GetParam(argc, argv, "p:w:b:m:d:ia", HELP_MSG, params) == EXIT_FAILURE) { 159 return EXIT_FAILURE; 160 } 161 auto iter = params.find('p'); 162 option.savePath_ = (iter != params.end()) ? iter->second : savePath; 163 auto iter2 = params.find('w'); 164 option.windowId_ = (iter2 != params.end()) ? iter2->second : ""; 165 auto iter3 = params.find('b'); 166 option.bundleName_ = (iter3 != params.end()) ? iter3->second : ""; 167 option.listWindows_ = params.find('i') != params.end(); 168 option.addExternAttr_ = params.find('a') != params.end(); 169 auto iter4 = params.find('m'); 170 option.notMergeWindow_ = (iter4 != params.end()) ? iter4->second == "false" : false; 171 auto iter5 = params.find('d'); 172 option.displayId_ = (iter5 != params.end()) ? std::atoi(iter5->second.c_str()): 0; 173 if (option.listWindows_ && option.addExternAttr_) { 174 PrintToConsole("The -a and -i options cannot be used together."); 175 return EXIT_FAILURE; 176 } 177 auto err = ApiCallErr(NO_ERROR); 178 DumpLayoutImpl(option, true, err); 179 if (err.code_ == NO_ERROR) { 180 PrintToConsole("DumpLayout saved to:" + option.savePath_); 181 return EXIT_SUCCESS; 182 } else if (err.code_ != ERR_INITIALIZE_FAILED) { 183 PrintToConsole("DumpLayout failed:" + err.message_); 184 return EXIT_FAILURE; 185 } 186 // Cannot connect to AAMS, broadcast request to running uitest-daemon if any 187 err = ApiCallErr(NO_ERROR); 188 auto cmd = OHOS::AAFwk::Want(); 189 cmd.SetParam("savePath", string(option.savePath_)); 190 cmd.SetParam("listWindows", option.listWindows_); 191 cmd.SetParam("addExternAttr", option.addExternAttr_); 192 cmd.SetParam("bundleName", string(option.bundleName_)); 193 cmd.SetParam("windowId", string(option.windowId_)); 194 cmd.SetParam("mergeWindow", option.notMergeWindow_); 195 cmd.SetParam("displayId", to_string(option.displayId_)); 196 ApiTransactor::SendBroadcastCommand(cmd, err); 197 if (err.code_ == NO_ERROR) { 198 PrintToConsole("DumpLayout saved to:" + option.savePath_); 199 } else { 200 PrintToConsole("DumpLayout failed:" + err.message_); 201 } 202 return err.code_ == NO_ERROR ? EXIT_SUCCESS : EXIT_FAILURE; 203 } 204 ScreenCap(int32_t argc,char * argv[])205 static int32_t ScreenCap(int32_t argc, char *argv[]) 206 { 207 auto ts = to_string(GetCurrentMicroseconds()); 208 auto savePath = "/data/local/tmp/screenCap_" + ts + ".png"; 209 auto displayId = 0; 210 map<char, string> params; 211 static constexpr string_view usage = "USAGE: uitest screenCap -p <path>"; 212 if (GetParam(argc, argv, "p:d:", usage, params) == EXIT_FAILURE) { 213 return EXIT_FAILURE; 214 } 215 auto iter = params.find('p'); 216 if (iter != params.end()) { 217 savePath = iter->second; 218 } 219 auto iter2 = params.find('d'); 220 if (iter2 != params.end()) { 221 displayId = std::atoi(iter2->second.c_str()); 222 } 223 auto controller = SysUiController(); 224 stringstream errorRecv; 225 auto fd = open(savePath.c_str(), O_RDWR | O_CREAT, 0666); 226 if (!controller.TakeScreenCap(fd, errorRecv, displayId)) { 227 PrintToConsole("ScreenCap failed: " + errorRecv.str()); 228 return EXIT_FAILURE; 229 } 230 PrintToConsole("ScreenCap saved to " + savePath); 231 (void) close(fd); 232 return EXIT_SUCCESS; 233 } 234 TranslateToken(string_view raw)235 static string TranslateToken(string_view raw) 236 { 237 if (raw.find_first_of('@') != string_view::npos) { 238 return string(raw); 239 } 240 return "default"; 241 } 242 GetOptionForCmd(const OHOS::AAFwk::Want & cmd)243 static DumpOption GetOptionForCmd(const OHOS::AAFwk::Want &cmd) 244 { 245 DumpOption option; 246 option.savePath_ = cmd.GetStringParam("savePath"); 247 option.listWindows_ = cmd.GetBoolParam("listWindows", false); 248 option.addExternAttr_ = cmd.GetBoolParam("addExternAttr", false); 249 option.bundleName_ = cmd.GetStringParam("bundleName"); 250 option.windowId_ = cmd.GetStringParam("windowId"); 251 option.notMergeWindow_ = cmd.GetBoolParam("mergeWindow", true); 252 option.displayId_ = atoi(cmd.GetStringParam("displayId").c_str()); 253 return option; 254 } 255 StartDaemon(string_view token,int32_t argc,char * argv[])256 static int32_t StartDaemon(string_view token, int32_t argc, char *argv[]) 257 { 258 if (token.empty()) { 259 LOG_E("Empty transaction token"); 260 return EXIT_FAILURE; 261 } 262 auto transalatedToken = TranslateToken(token); 263 if (daemon(0, 0) != 0) { 264 LOG_E("Failed to daemonize current process"); 265 return EXIT_FAILURE; 266 } 267 LOG_I("Server starting up"); 268 UiDriver::RegisterController(make_unique<SysUiController>()); 269 // accept remopte dump request during deamon running (initController=false) 270 ApiTransactor::SetBroadcastCommandHandler([] (const OHOS::AAFwk::Want &cmd, ApiCallErr &err) { 271 auto option = GetOptionForCmd(cmd); 272 DumpLayoutImpl(option, false, err); 273 }); 274 if (token == "singleness") { 275 ExecuteExtension(VERSION, argc, argv); 276 LOG_I("Server exit"); 277 ApiTransactor::UnsetBroadcastCommandHandler(); 278 _Exit(0); 279 return 0; 280 } 281 ApiTransactor apiTransactServer(true); 282 auto &apiServer = FrontendApiServer::Get(); 283 auto apiHandler = std::bind(&FrontendApiServer::Call, &apiServer, placeholders::_1, placeholders::_2); 284 auto cbHandler = std::bind(&ApiTransactor::Transact, &apiTransactServer, placeholders::_1, placeholders::_2); 285 apiServer.SetCallbackHandler(cbHandler); // used for callback from server to client 286 if (!apiTransactServer.InitAndConnectPeer(transalatedToken, apiHandler)) { 287 LOG_E("Failed to initialize server"); 288 ApiTransactor::UnsetBroadcastCommandHandler(); 289 _Exit(0); 290 return EXIT_FAILURE; 291 } 292 mutex mtx; 293 unique_lock<mutex> lock(mtx); 294 condition_variable condVar; 295 apiTransactServer.SetDeathCallback([&condVar]() { 296 condVar.notify_one(); 297 }); 298 LOG_I("UiTest-daemon running, pid=%{public}d", getpid()); 299 condVar.wait(lock); 300 LOG_I("Server exit"); 301 apiTransactServer.Finalize(); 302 ApiTransactor::UnsetBroadcastCommandHandler(); 303 _Exit(0); 304 return 0; 305 } 306 UiRecord(int32_t argc,char * argv[])307 static int32_t UiRecord(int32_t argc, char *argv[]) 308 { 309 static constexpr string_view usage = "USAGE: uitest uiRecord <read|record>"; 310 if (!(argc == INDEX_THREE || argc == INDEX_FOUR)) { 311 PrintToConsole("Missing parameter. \n"); 312 PrintToConsole(usage); 313 return EXIT_FAILURE; 314 } 315 std::string opt = argv[TWO]; 316 std::string modeOpt; 317 if (argc == INDEX_FOUR) { 318 modeOpt = argv[THREE]; 319 } 320 if (opt == "record") { 321 auto controller = make_unique<SysUiController>(); 322 ApiCallErr error = ApiCallErr(NO_ERROR); 323 if (!controller->ConnectToSysAbility(error)) { 324 PrintToConsole(error.message_); 325 return EXIT_FAILURE; 326 } 327 UiDriver::RegisterController(move(controller)); 328 return UiDriverRecordStart(modeOpt); 329 } else if (opt == "read") { 330 EventData::ReadEventLine(); 331 return OHOS::ERR_OK; 332 } else { 333 PrintToConsole("Illegal argument: " + opt); 334 PrintToConsole(usage); 335 return EXIT_FAILURE; 336 } 337 } 338 UiInput(int32_t argc,char * argv[])339 static int32_t UiInput(int32_t argc, char *argv[]) 340 { 341 if ((size_t)argc < INDEX_FOUR) { 342 std::cout << "Missing parameter. \n" << std::endl; 343 PrintInputMessage(); 344 return EXIT_FAILURE; 345 } 346 if ((string)argv[THREE] == "help") { 347 PrintInputMessage(); 348 return OHOS::ERR_OK; 349 } 350 auto controller = make_unique<SysUiController>(); 351 UiDriver::RegisterController(move(controller)); 352 return UiActionInput(argc, argv); 353 } 354 main(int32_t argc,char * argv[])355 extern "C" int32_t main(int32_t argc, char *argv[]) 356 { 357 if ((size_t)argc < INDEX_TWO) { 358 PrintToConsole("Missing argument"); 359 PrintToConsole(HELP_MSG); 360 _Exit(EXIT_FAILURE); 361 } 362 string command(argv[1]); 363 if (command == "dumpLayout") { 364 _Exit(DumpLayout(argc, argv)); 365 } else if (command == "start-daemon") { 366 string_view token = argc < 3 ? "" : argv[2]; 367 _Exit(StartDaemon(token, argc - THREE, argv + THREE)); 368 } else if (command == "screenCap") { 369 _Exit(ScreenCap(argc, argv)); 370 } else if (command == "uiRecord") { 371 _Exit(UiRecord(argc, argv)); 372 } else if (command == "uiInput") { 373 _Exit(UiInput(argc, argv)); 374 } else if (command == "--version") { 375 PrintToConsole(VERSION); 376 _Exit(EXIT_SUCCESS); 377 } else if (command == "help") { 378 PrintToConsole(HELP_MSG); 379 _Exit(EXIT_SUCCESS); 380 } else { 381 PrintToConsole("Illegal argument: " + command); 382 PrintToConsole(HELP_MSG); 383 _Exit(EXIT_FAILURE); 384 } 385 } 386 } 387