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\n" 57 "dumpLayout get the current layout information\n" 58 " -p savepath\n" 59 " -i not merge windows and filter nodes\n" 60 " -a include font attributes\n" 61 "start-daemon <token> start the test process\n" 62 "uiRecord \n" 63 " record wirte location coordinates of events into files\n" 64 " read print file content to the console\n" 65 "uiInput \n" 66 " help print uiInput usage\n" 67 " dircFling [velocity stepLength] direction ranges from 0,1,2,3 (left, right, up, down)\n" 68 " click/doubleClick/longClick <x> <y> click on the target coordinates\n" 69 " swipe/drag <from_x> <from_y> <to_x> <to_y> [velocity] velocity ranges from 200 to 40000, default 600\n" 70 " fling <from_x> <from_y> <to_x> <to_y> [velocity] velocity ranges from 200 to 40000, default 600\n" 71 " keyEvent <keyID/Back/Home/Power> inject keyEvent\n" 72 " keyEvent <keyID_0> <keyID_1> [keyID_2] keyID_2 default to None \n" 73 " inputText <x> <y> <text> inputText at the target coordinate point\n" 74 "--version print current tool version\n"; 75 76 const std::string VERSION = "5.0.1.2"; 77 struct option g_longoptions[] = { 78 {"save file in this path", required_argument, nullptr, 'p'}, 79 {"dump all UI trees in json array format", no_argument, nullptr, 'I'} 80 }; 81 /* *Print to the console of this shell process. */ PrintToConsole(string_view message)82 static inline void PrintToConsole(string_view message) 83 { 84 std::cout << message << std::endl; 85 } 86 GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, map<char, string> ¶ms)87 static int32_t GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, 88 map<char, string> ¶ms) 89 { 90 int opt; 91 while ((opt = getopt_long(argc, argv, optstring.data(), g_longoptions, nullptr)) != -1) { 92 switch (opt) { 93 case '?': 94 PrintToConsole(usage); 95 return EXIT_FAILURE; 96 case 'i': 97 params.insert(pair<char, string>(opt, "true")); 98 break; 99 case 'a': 100 params.insert(pair<char, string>(opt, "true")); 101 break; 102 default: 103 params.insert(pair<char, string>(opt, optarg)); 104 break; 105 } 106 } 107 return EXIT_SUCCESS; 108 } 109 DumpLayoutImpl(string_view path, bool listWindows, bool initController, bool addExternAttr, ApiCallErr &err)110 static void DumpLayoutImpl(string_view path, bool listWindows, bool initController, bool addExternAttr, 111 ApiCallErr &err) 112 { 113 ofstream fout; 114 fout.open(path, ios::out | ios::binary); 115 if (!fout) { 116 err = ApiCallErr(ERR_INVALID_INPUT, "Error path:" + string(path) + strerror(errno)); 117 return; 118 } 119 if (initController) { 120 UiDriver::RegisterController(make_unique<SysUiController>()); 121 } 122 auto driver = UiDriver(); 123 auto data = nlohmann::json(); 124 driver.DumpUiHierarchy(data, listWindows, addExternAttr, err); 125 if (err.code_ != NO_ERROR) { 126 fout.close(); 127 return; 128 } 129 fout << data.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); 130 fout.close(); 131 return; 132 } 133 DumpLayout(int32_t argc, char *argv[])134 static int32_t DumpLayout(int32_t argc, char *argv[]) 135 { 136 auto ts = to_string(GetCurrentMicroseconds()); 137 auto savePath = "/data/local/tmp/layout_" + ts + ".json"; 138 map<char, string> params; 139 static constexpr string_view usage = "USAGE: uitestkit dumpLayout -p <path>"; 140 if (GetParam(argc, argv, "p:ia", usage, params) == EXIT_FAILURE) { 141 return EXIT_FAILURE; 142 } 143 auto iter = params.find('p'); 144 if (iter != params.end()) { 145 savePath = iter->second; 146 } 147 const bool listWindows = params.find('i') != params.end(); 148 const bool addExternAttr = params.find('a') != params.end(); 149 auto err = ApiCallErr(NO_ERROR); 150 DumpLayoutImpl(savePath, listWindows, true, addExternAttr, err); 151 if (err.code_ == NO_ERROR) { 152 PrintToConsole("DumpLayout saved to:" + savePath); 153 return EXIT_SUCCESS; 154 } else if (err.code_ != ERR_INITIALIZE_FAILED) { 155 PrintToConsole("DumpLayout failed:" + err.message_); 156 return EXIT_FAILURE; 157 } 158 // Cannot connect to AAMS, broadcast request to running uitest-daemon if any 159 err = ApiCallErr(NO_ERROR); 160 auto cmd = OHOS::AAFwk::Want(); 161 cmd.SetParam("savePath", string(savePath)); 162 cmd.SetParam("listWindows", listWindows); 163 ApiTransactor::SendBroadcastCommand(cmd, err); 164 if (err.code_ == NO_ERROR) { 165 PrintToConsole("DumpLayout saved to:" + savePath); 166 return EXIT_SUCCESS; 167 } else { 168 PrintToConsole("DumpLayout failed:" + err.message_); 169 return EXIT_FAILURE; 170 } 171 } 172 ScreenCap(int32_t argc, char *argv[])173 static int32_t ScreenCap(int32_t argc, char *argv[]) 174 { 175 auto ts = to_string(GetCurrentMicroseconds()); 176 auto savePath = "/data/local/tmp/screenCap_" + ts + ".png"; 177 map<char, string> params; 178 static constexpr string_view usage = "USAGE: uitest screenCap -p <path>"; 179 if (GetParam(argc, argv, "p:", usage, params) == EXIT_FAILURE) { 180 return EXIT_FAILURE; 181 } 182 auto iter = params.find('p'); 183 if (iter != params.end()) { 184 savePath = iter->second; 185 } 186 auto controller = SysUiController(); 187 stringstream errorRecv; 188 auto fd = open(savePath.c_str(), O_RDWR | O_CREAT, 0666); 189 if (!controller.TakeScreenCap(fd, errorRecv)) { 190 PrintToConsole("ScreenCap failed: " + errorRecv.str()); 191 return EXIT_FAILURE; 192 } 193 PrintToConsole("ScreenCap saved to " + savePath); 194 (void) close(fd); 195 return EXIT_SUCCESS; 196 } 197 TranslateToken(string_view raw)198 static string TranslateToken(string_view raw) 199 { 200 if (raw.find_first_of('@') != string_view::npos) { 201 return string(raw); 202 } 203 return "default"; 204 } 205 StartDaemon(string_view token, int32_t argc, char *argv[])206 static int32_t StartDaemon(string_view token, int32_t argc, char *argv[]) 207 { 208 if (token.empty()) { 209 LOG_E("Empty transaction token"); 210 return EXIT_FAILURE; 211 } 212 auto transalatedToken = TranslateToken(token); 213 if (daemon(0, 0) != 0) { 214 LOG_E("Failed to daemonize current process"); 215 return EXIT_FAILURE; 216 } 217 LOG_I("Server starting up"); 218 UiDriver::RegisterController(make_unique<SysUiController>()); 219 // accept remopte dump request during deamon running (initController=false) 220 ApiTransactor::SetBroadcastCommandHandler([] (const OHOS::AAFwk::Want &cmd, ApiCallErr &err) { 221 DumpLayoutImpl(cmd.GetStringParam("savePath"), cmd.GetBoolParam("listWindows", false), false, false, err); 222 }); 223 std::string_view singlenessToken = "singleness"; 224 if (token == singlenessToken) { 225 ExecuteExtension(VERSION, argc, argv); 226 LOG_I("Server exit"); 227 ApiTransactor::UnsetBroadcastCommandHandler(); 228 _Exit(0); 229 return 0; 230 } 231 ApiTransactor apiTransactServer(true); 232 auto &apiServer = FrontendApiServer::Get(); 233 auto apiHandler = std::bind(&FrontendApiServer::Call, &apiServer, placeholders::_1, placeholders::_2); 234 auto cbHandler = std::bind(&ApiTransactor::Transact, &apiTransactServer, placeholders::_1, placeholders::_2); 235 apiServer.SetCallbackHandler(cbHandler); // used for callback from server to client 236 if (!apiTransactServer.InitAndConnectPeer(transalatedToken, apiHandler)) { 237 LOG_E("Failed to initialize server"); 238 ApiTransactor::UnsetBroadcastCommandHandler(); 239 _Exit(0); 240 return EXIT_FAILURE; 241 } 242 mutex mtx; 243 unique_lock<mutex> lock(mtx); 244 condition_variable condVar; 245 apiTransactServer.SetDeathCallback([&condVar]() { 246 condVar.notify_one(); 247 }); 248 LOG_I("UiTest-daemon running, pid=%{public}d", getpid()); 249 condVar.wait(lock); 250 LOG_I("Server exit"); 251 apiTransactServer.Finalize(); 252 ApiTransactor::UnsetBroadcastCommandHandler(); 253 _Exit(0); 254 return 0; 255 } 256 UiRecord(int32_t argc, char *argv[])257 static int32_t UiRecord(int32_t argc, char *argv[]) 258 { 259 static constexpr string_view usage = "USAGE: uitest uiRecord <read|record>"; 260 if (!(argc == INDEX_THREE || argc == INDEX_FOUR)) { 261 PrintToConsole("Missing parameter. \n"); 262 PrintToConsole(usage); 263 return EXIT_FAILURE; 264 } 265 std::string opt = argv[TWO]; 266 std::string modeOpt; 267 if (argc == INDEX_FOUR) { 268 modeOpt = argv[THREE]; 269 } 270 if (opt == "record") { 271 auto controller = make_unique<SysUiController>(); 272 ApiCallErr error = ApiCallErr(NO_ERROR); 273 if (!controller->ConnectToSysAbility(error)) { 274 PrintToConsole(error.message_); 275 return EXIT_FAILURE; 276 } 277 UiDriver::RegisterController(move(controller)); 278 return UiDriverRecordStart(modeOpt); 279 } else if (opt == "read") { 280 EventData::ReadEventLine(); 281 return OHOS::ERR_OK; 282 } else { 283 PrintToConsole("Illegal argument: " + opt); 284 PrintToConsole(usage); 285 return EXIT_FAILURE; 286 } 287 } 288 UiInput(int32_t argc, char *argv[])289 static int32_t UiInput(int32_t argc, char *argv[]) 290 { 291 if ((size_t)argc < INDEX_FOUR) { 292 std::cout << "Missing parameter. \n" << std::endl; 293 PrintInputMessage(); 294 return EXIT_FAILURE; 295 } 296 if ((string)argv[THREE] == "help") { 297 PrintInputMessage(); 298 return OHOS::ERR_OK; 299 } 300 auto controller = make_unique<SysUiController>(); 301 UiDriver::RegisterController(move(controller)); 302 return UiActionInput(argc, argv); 303 } 304 main(int32_t argc, char *argv[])305 extern "C" int32_t main(int32_t argc, char *argv[]) 306 { 307 if ((size_t)argc < INDEX_TWO) { 308 PrintToConsole("Missing argument"); 309 PrintToConsole(HELP_MSG); 310 _Exit(EXIT_FAILURE); 311 } 312 string command(argv[1]); 313 if (command == "dumpLayout") { 314 _Exit(DumpLayout(argc, argv)); 315 } else if (command == "start-daemon") { 316 string_view token = argc < 3 ? "" : argv[2]; 317 _Exit(StartDaemon(token, argc - THREE, argv + THREE)); 318 } else if (command == "screenCap") { 319 _Exit(ScreenCap(argc, argv)); 320 } else if (command == "uiRecord") { 321 _Exit(UiRecord(argc, argv)); 322 } else if (command == "uiInput") { 323 _Exit(UiInput(argc, argv)); 324 } else if (command == "--version") { 325 PrintToConsole(VERSION); 326 _Exit(EXIT_SUCCESS); 327 } else if (command == "help") { 328 PrintToConsole(HELP_MSG); 329 _Exit(EXIT_SUCCESS); 330 } else { 331 PrintToConsole("Illegal argument: " + command); 332 PrintToConsole(HELP_MSG); 333 _Exit(EXIT_FAILURE); 334 } 335 } 336 } 337