1886da342Sopenharmony_ci/* 2886da342Sopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3886da342Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4886da342Sopenharmony_ci * you may not use this file except in compliance with the License. 5886da342Sopenharmony_ci * You may obtain a copy of the License at 6886da342Sopenharmony_ci * 7886da342Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8886da342Sopenharmony_ci * 9886da342Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10886da342Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11886da342Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12886da342Sopenharmony_ci * See the License for the specific language governing permissions and 13886da342Sopenharmony_ci * limitations under the License. 14886da342Sopenharmony_ci */ 15886da342Sopenharmony_ci 16886da342Sopenharmony_ci#include <chrono> 17886da342Sopenharmony_ci#include <unistd.h> 18886da342Sopenharmony_ci#include <memory> 19886da342Sopenharmony_ci#include <iostream> 20886da342Sopenharmony_ci#include <fstream> 21886da342Sopenharmony_ci#include <getopt.h> 22886da342Sopenharmony_ci#include <dirent.h> 23886da342Sopenharmony_ci#include <sys/stat.h> 24886da342Sopenharmony_ci#include <typeinfo> 25886da342Sopenharmony_ci#include <cstring> 26886da342Sopenharmony_ci#include <vector> 27886da342Sopenharmony_ci#include <functional> 28886da342Sopenharmony_ci#include <atomic> 29886da342Sopenharmony_ci#include <mutex> 30886da342Sopenharmony_ci#include <ctime> 31886da342Sopenharmony_ci#include <condition_variable> 32886da342Sopenharmony_ci#include <cmath> 33886da342Sopenharmony_ci#include <string> 34886da342Sopenharmony_ci#include <vector> 35886da342Sopenharmony_ci#include <cmath> 36886da342Sopenharmony_ci#include <fcntl.h> 37886da342Sopenharmony_ci#include "ipc_transactor.h" 38886da342Sopenharmony_ci#include "system_ui_controller.h" 39886da342Sopenharmony_ci#include "input_manager.h" 40886da342Sopenharmony_ci#include "i_input_event_consumer.h" 41886da342Sopenharmony_ci#include "pointer_event.h" 42886da342Sopenharmony_ci#include "ui_driver.h" 43886da342Sopenharmony_ci#include "ui_record.h" 44886da342Sopenharmony_ci#include "ui_input.h" 45886da342Sopenharmony_ci#include "ui_model.h" 46886da342Sopenharmony_ci#include "extension_executor.h" 47886da342Sopenharmony_ci 48886da342Sopenharmony_ciusing namespace std; 49886da342Sopenharmony_ciusing namespace std::chrono; 50886da342Sopenharmony_ci 51886da342Sopenharmony_cinamespace OHOS::uitest { 52886da342Sopenharmony_ci const std::string HELP_MSG = 53886da342Sopenharmony_ci "usage: uitest <command> [options] \n" 54886da342Sopenharmony_ci "help print help messages\n" 55886da342Sopenharmony_ci "screenCap capture the current screen\n" 56886da342Sopenharmony_ci " -p savepath\n" 57886da342Sopenharmony_ci "dumpLayout get the current layout information\n" 58886da342Sopenharmony_ci " -p savepath\n" 59886da342Sopenharmony_ci " -i not merge windows and filter nodes\n" 60886da342Sopenharmony_ci " -a include font attributes\n" 61886da342Sopenharmony_ci "start-daemon <token> start the test process\n" 62886da342Sopenharmony_ci "uiRecord \n" 63886da342Sopenharmony_ci " record wirte location coordinates of events into files\n" 64886da342Sopenharmony_ci " read print file content to the console\n" 65886da342Sopenharmony_ci "uiInput \n" 66886da342Sopenharmony_ci " help print uiInput usage\n" 67886da342Sopenharmony_ci " dircFling [velocity stepLength] direction ranges from 0,1,2,3 (left, right, up, down)\n" 68886da342Sopenharmony_ci " click/doubleClick/longClick <x> <y> click on the target coordinates\n" 69886da342Sopenharmony_ci " swipe/drag <from_x> <from_y> <to_x> <to_y> [velocity] velocity ranges from 200 to 40000, default 600\n" 70886da342Sopenharmony_ci " fling <from_x> <from_y> <to_x> <to_y> [velocity] velocity ranges from 200 to 40000, default 600\n" 71886da342Sopenharmony_ci " keyEvent <keyID/Back/Home/Power> inject keyEvent\n" 72886da342Sopenharmony_ci " keyEvent <keyID_0> <keyID_1> [keyID_2] keyID_2 default to None \n" 73886da342Sopenharmony_ci " inputText <x> <y> <text> inputText at the target coordinate point\n" 74886da342Sopenharmony_ci "--version print current tool version\n"; 75886da342Sopenharmony_ci 76886da342Sopenharmony_ci const std::string VERSION = "5.0.1.2"; 77886da342Sopenharmony_ci struct option g_longoptions[] = { 78886da342Sopenharmony_ci {"save file in this path", required_argument, nullptr, 'p'}, 79886da342Sopenharmony_ci {"dump all UI trees in json array format", no_argument, nullptr, 'I'} 80886da342Sopenharmony_ci }; 81886da342Sopenharmony_ci /* *Print to the console of this shell process. */ 82886da342Sopenharmony_ci static inline void PrintToConsole(string_view message) 83886da342Sopenharmony_ci { 84886da342Sopenharmony_ci std::cout << message << std::endl; 85886da342Sopenharmony_ci } 86886da342Sopenharmony_ci 87886da342Sopenharmony_ci static int32_t GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, 88886da342Sopenharmony_ci map<char, string> ¶ms) 89886da342Sopenharmony_ci { 90886da342Sopenharmony_ci int opt; 91886da342Sopenharmony_ci while ((opt = getopt_long(argc, argv, optstring.data(), g_longoptions, nullptr)) != -1) { 92886da342Sopenharmony_ci switch (opt) { 93886da342Sopenharmony_ci case '?': 94886da342Sopenharmony_ci PrintToConsole(usage); 95886da342Sopenharmony_ci return EXIT_FAILURE; 96886da342Sopenharmony_ci case 'i': 97886da342Sopenharmony_ci params.insert(pair<char, string>(opt, "true")); 98886da342Sopenharmony_ci break; 99886da342Sopenharmony_ci case 'a': 100886da342Sopenharmony_ci params.insert(pair<char, string>(opt, "true")); 101886da342Sopenharmony_ci break; 102886da342Sopenharmony_ci default: 103886da342Sopenharmony_ci params.insert(pair<char, string>(opt, optarg)); 104886da342Sopenharmony_ci break; 105886da342Sopenharmony_ci } 106886da342Sopenharmony_ci } 107886da342Sopenharmony_ci return EXIT_SUCCESS; 108886da342Sopenharmony_ci } 109886da342Sopenharmony_ci 110886da342Sopenharmony_ci static void DumpLayoutImpl(string_view path, bool listWindows, bool initController, bool addExternAttr, 111886da342Sopenharmony_ci ApiCallErr &err) 112886da342Sopenharmony_ci { 113886da342Sopenharmony_ci ofstream fout; 114886da342Sopenharmony_ci fout.open(path, ios::out | ios::binary); 115886da342Sopenharmony_ci if (!fout) { 116886da342Sopenharmony_ci err = ApiCallErr(ERR_INVALID_INPUT, "Error path:" + string(path) + strerror(errno)); 117886da342Sopenharmony_ci return; 118886da342Sopenharmony_ci } 119886da342Sopenharmony_ci if (initController) { 120886da342Sopenharmony_ci UiDriver::RegisterController(make_unique<SysUiController>()); 121886da342Sopenharmony_ci } 122886da342Sopenharmony_ci auto driver = UiDriver(); 123886da342Sopenharmony_ci auto data = nlohmann::json(); 124886da342Sopenharmony_ci driver.DumpUiHierarchy(data, listWindows, addExternAttr, err); 125886da342Sopenharmony_ci if (err.code_ != NO_ERROR) { 126886da342Sopenharmony_ci fout.close(); 127886da342Sopenharmony_ci return; 128886da342Sopenharmony_ci } 129886da342Sopenharmony_ci fout << data.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); 130886da342Sopenharmony_ci fout.close(); 131886da342Sopenharmony_ci return; 132886da342Sopenharmony_ci } 133886da342Sopenharmony_ci 134886da342Sopenharmony_ci static int32_t DumpLayout(int32_t argc, char *argv[]) 135886da342Sopenharmony_ci { 136886da342Sopenharmony_ci auto ts = to_string(GetCurrentMicroseconds()); 137886da342Sopenharmony_ci auto savePath = "/data/local/tmp/layout_" + ts + ".json"; 138886da342Sopenharmony_ci map<char, string> params; 139886da342Sopenharmony_ci static constexpr string_view usage = "USAGE: uitestkit dumpLayout -p <path>"; 140886da342Sopenharmony_ci if (GetParam(argc, argv, "p:ia", usage, params) == EXIT_FAILURE) { 141886da342Sopenharmony_ci return EXIT_FAILURE; 142886da342Sopenharmony_ci } 143886da342Sopenharmony_ci auto iter = params.find('p'); 144886da342Sopenharmony_ci if (iter != params.end()) { 145886da342Sopenharmony_ci savePath = iter->second; 146886da342Sopenharmony_ci } 147886da342Sopenharmony_ci const bool listWindows = params.find('i') != params.end(); 148886da342Sopenharmony_ci const bool addExternAttr = params.find('a') != params.end(); 149886da342Sopenharmony_ci auto err = ApiCallErr(NO_ERROR); 150886da342Sopenharmony_ci DumpLayoutImpl(savePath, listWindows, true, addExternAttr, err); 151886da342Sopenharmony_ci if (err.code_ == NO_ERROR) { 152886da342Sopenharmony_ci PrintToConsole("DumpLayout saved to:" + savePath); 153886da342Sopenharmony_ci return EXIT_SUCCESS; 154886da342Sopenharmony_ci } else if (err.code_ != ERR_INITIALIZE_FAILED) { 155886da342Sopenharmony_ci PrintToConsole("DumpLayout failed:" + err.message_); 156886da342Sopenharmony_ci return EXIT_FAILURE; 157886da342Sopenharmony_ci } 158886da342Sopenharmony_ci // Cannot connect to AAMS, broadcast request to running uitest-daemon if any 159886da342Sopenharmony_ci err = ApiCallErr(NO_ERROR); 160886da342Sopenharmony_ci auto cmd = OHOS::AAFwk::Want(); 161886da342Sopenharmony_ci cmd.SetParam("savePath", string(savePath)); 162886da342Sopenharmony_ci cmd.SetParam("listWindows", listWindows); 163886da342Sopenharmony_ci ApiTransactor::SendBroadcastCommand(cmd, err); 164886da342Sopenharmony_ci if (err.code_ == NO_ERROR) { 165886da342Sopenharmony_ci PrintToConsole("DumpLayout saved to:" + savePath); 166886da342Sopenharmony_ci return EXIT_SUCCESS; 167886da342Sopenharmony_ci } else { 168886da342Sopenharmony_ci PrintToConsole("DumpLayout failed:" + err.message_); 169886da342Sopenharmony_ci return EXIT_FAILURE; 170886da342Sopenharmony_ci } 171886da342Sopenharmony_ci } 172886da342Sopenharmony_ci 173886da342Sopenharmony_ci static int32_t ScreenCap(int32_t argc, char *argv[]) 174886da342Sopenharmony_ci { 175886da342Sopenharmony_ci auto ts = to_string(GetCurrentMicroseconds()); 176886da342Sopenharmony_ci auto savePath = "/data/local/tmp/screenCap_" + ts + ".png"; 177886da342Sopenharmony_ci map<char, string> params; 178886da342Sopenharmony_ci static constexpr string_view usage = "USAGE: uitest screenCap -p <path>"; 179886da342Sopenharmony_ci if (GetParam(argc, argv, "p:", usage, params) == EXIT_FAILURE) { 180886da342Sopenharmony_ci return EXIT_FAILURE; 181886da342Sopenharmony_ci } 182886da342Sopenharmony_ci auto iter = params.find('p'); 183886da342Sopenharmony_ci if (iter != params.end()) { 184886da342Sopenharmony_ci savePath = iter->second; 185886da342Sopenharmony_ci } 186886da342Sopenharmony_ci auto controller = SysUiController(); 187886da342Sopenharmony_ci stringstream errorRecv; 188886da342Sopenharmony_ci auto fd = open(savePath.c_str(), O_RDWR | O_CREAT, 0666); 189886da342Sopenharmony_ci if (!controller.TakeScreenCap(fd, errorRecv)) { 190886da342Sopenharmony_ci PrintToConsole("ScreenCap failed: " + errorRecv.str()); 191886da342Sopenharmony_ci return EXIT_FAILURE; 192886da342Sopenharmony_ci } 193886da342Sopenharmony_ci PrintToConsole("ScreenCap saved to " + savePath); 194886da342Sopenharmony_ci (void) close(fd); 195886da342Sopenharmony_ci return EXIT_SUCCESS; 196886da342Sopenharmony_ci } 197886da342Sopenharmony_ci 198886da342Sopenharmony_ci static string TranslateToken(string_view raw) 199886da342Sopenharmony_ci { 200886da342Sopenharmony_ci if (raw.find_first_of('@') != string_view::npos) { 201886da342Sopenharmony_ci return string(raw); 202886da342Sopenharmony_ci } 203886da342Sopenharmony_ci return "default"; 204886da342Sopenharmony_ci } 205886da342Sopenharmony_ci 206886da342Sopenharmony_ci static int32_t StartDaemon(string_view token, int32_t argc, char *argv[]) 207886da342Sopenharmony_ci { 208886da342Sopenharmony_ci if (token.empty()) { 209886da342Sopenharmony_ci LOG_E("Empty transaction token"); 210886da342Sopenharmony_ci return EXIT_FAILURE; 211886da342Sopenharmony_ci } 212886da342Sopenharmony_ci auto transalatedToken = TranslateToken(token); 213886da342Sopenharmony_ci if (daemon(0, 0) != 0) { 214886da342Sopenharmony_ci LOG_E("Failed to daemonize current process"); 215886da342Sopenharmony_ci return EXIT_FAILURE; 216886da342Sopenharmony_ci } 217886da342Sopenharmony_ci LOG_I("Server starting up"); 218886da342Sopenharmony_ci UiDriver::RegisterController(make_unique<SysUiController>()); 219886da342Sopenharmony_ci // accept remopte dump request during deamon running (initController=false) 220886da342Sopenharmony_ci ApiTransactor::SetBroadcastCommandHandler([] (const OHOS::AAFwk::Want &cmd, ApiCallErr &err) { 221886da342Sopenharmony_ci DumpLayoutImpl(cmd.GetStringParam("savePath"), cmd.GetBoolParam("listWindows", false), false, false, err); 222886da342Sopenharmony_ci }); 223886da342Sopenharmony_ci std::string_view singlenessToken = "singleness"; 224886da342Sopenharmony_ci if (token == singlenessToken) { 225886da342Sopenharmony_ci ExecuteExtension(VERSION, argc, argv); 226886da342Sopenharmony_ci LOG_I("Server exit"); 227886da342Sopenharmony_ci ApiTransactor::UnsetBroadcastCommandHandler(); 228886da342Sopenharmony_ci _Exit(0); 229886da342Sopenharmony_ci return 0; 230886da342Sopenharmony_ci } 231886da342Sopenharmony_ci ApiTransactor apiTransactServer(true); 232886da342Sopenharmony_ci auto &apiServer = FrontendApiServer::Get(); 233886da342Sopenharmony_ci auto apiHandler = std::bind(&FrontendApiServer::Call, &apiServer, placeholders::_1, placeholders::_2); 234886da342Sopenharmony_ci auto cbHandler = std::bind(&ApiTransactor::Transact, &apiTransactServer, placeholders::_1, placeholders::_2); 235886da342Sopenharmony_ci apiServer.SetCallbackHandler(cbHandler); // used for callback from server to client 236886da342Sopenharmony_ci if (!apiTransactServer.InitAndConnectPeer(transalatedToken, apiHandler)) { 237886da342Sopenharmony_ci LOG_E("Failed to initialize server"); 238886da342Sopenharmony_ci ApiTransactor::UnsetBroadcastCommandHandler(); 239886da342Sopenharmony_ci _Exit(0); 240886da342Sopenharmony_ci return EXIT_FAILURE; 241886da342Sopenharmony_ci } 242886da342Sopenharmony_ci mutex mtx; 243886da342Sopenharmony_ci unique_lock<mutex> lock(mtx); 244886da342Sopenharmony_ci condition_variable condVar; 245886da342Sopenharmony_ci apiTransactServer.SetDeathCallback([&condVar]() { 246886da342Sopenharmony_ci condVar.notify_one(); 247886da342Sopenharmony_ci }); 248886da342Sopenharmony_ci LOG_I("UiTest-daemon running, pid=%{public}d", getpid()); 249886da342Sopenharmony_ci condVar.wait(lock); 250886da342Sopenharmony_ci LOG_I("Server exit"); 251886da342Sopenharmony_ci apiTransactServer.Finalize(); 252886da342Sopenharmony_ci ApiTransactor::UnsetBroadcastCommandHandler(); 253886da342Sopenharmony_ci _Exit(0); 254886da342Sopenharmony_ci return 0; 255886da342Sopenharmony_ci } 256886da342Sopenharmony_ci 257886da342Sopenharmony_ci static int32_t UiRecord(int32_t argc, char *argv[]) 258886da342Sopenharmony_ci { 259886da342Sopenharmony_ci static constexpr string_view usage = "USAGE: uitest uiRecord <read|record>"; 260886da342Sopenharmony_ci if (!(argc == INDEX_THREE || argc == INDEX_FOUR)) { 261886da342Sopenharmony_ci PrintToConsole("Missing parameter. \n"); 262886da342Sopenharmony_ci PrintToConsole(usage); 263886da342Sopenharmony_ci return EXIT_FAILURE; 264886da342Sopenharmony_ci } 265886da342Sopenharmony_ci std::string opt = argv[TWO]; 266886da342Sopenharmony_ci std::string modeOpt; 267886da342Sopenharmony_ci if (argc == INDEX_FOUR) { 268886da342Sopenharmony_ci modeOpt = argv[THREE]; 269886da342Sopenharmony_ci } 270886da342Sopenharmony_ci if (opt == "record") { 271886da342Sopenharmony_ci auto controller = make_unique<SysUiController>(); 272886da342Sopenharmony_ci ApiCallErr error = ApiCallErr(NO_ERROR); 273886da342Sopenharmony_ci if (!controller->ConnectToSysAbility(error)) { 274886da342Sopenharmony_ci PrintToConsole(error.message_); 275886da342Sopenharmony_ci return EXIT_FAILURE; 276886da342Sopenharmony_ci } 277886da342Sopenharmony_ci UiDriver::RegisterController(move(controller)); 278886da342Sopenharmony_ci return UiDriverRecordStart(modeOpt); 279886da342Sopenharmony_ci } else if (opt == "read") { 280886da342Sopenharmony_ci EventData::ReadEventLine(); 281886da342Sopenharmony_ci return OHOS::ERR_OK; 282886da342Sopenharmony_ci } else { 283886da342Sopenharmony_ci PrintToConsole("Illegal argument: " + opt); 284886da342Sopenharmony_ci PrintToConsole(usage); 285886da342Sopenharmony_ci return EXIT_FAILURE; 286886da342Sopenharmony_ci } 287886da342Sopenharmony_ci } 288886da342Sopenharmony_ci 289886da342Sopenharmony_ci static int32_t UiInput(int32_t argc, char *argv[]) 290886da342Sopenharmony_ci { 291886da342Sopenharmony_ci if ((size_t)argc < INDEX_FOUR) { 292886da342Sopenharmony_ci std::cout << "Missing parameter. \n" << std::endl; 293886da342Sopenharmony_ci PrintInputMessage(); 294886da342Sopenharmony_ci return EXIT_FAILURE; 295886da342Sopenharmony_ci } 296886da342Sopenharmony_ci if ((string)argv[THREE] == "help") { 297886da342Sopenharmony_ci PrintInputMessage(); 298886da342Sopenharmony_ci return OHOS::ERR_OK; 299886da342Sopenharmony_ci } 300886da342Sopenharmony_ci auto controller = make_unique<SysUiController>(); 301886da342Sopenharmony_ci UiDriver::RegisterController(move(controller)); 302886da342Sopenharmony_ci return UiActionInput(argc, argv); 303886da342Sopenharmony_ci } 304886da342Sopenharmony_ci 305886da342Sopenharmony_ci extern "C" int32_t main(int32_t argc, char *argv[]) 306886da342Sopenharmony_ci { 307886da342Sopenharmony_ci if ((size_t)argc < INDEX_TWO) { 308886da342Sopenharmony_ci PrintToConsole("Missing argument"); 309886da342Sopenharmony_ci PrintToConsole(HELP_MSG); 310886da342Sopenharmony_ci _Exit(EXIT_FAILURE); 311886da342Sopenharmony_ci } 312886da342Sopenharmony_ci string command(argv[1]); 313886da342Sopenharmony_ci if (command == "dumpLayout") { 314886da342Sopenharmony_ci _Exit(DumpLayout(argc, argv)); 315886da342Sopenharmony_ci } else if (command == "start-daemon") { 316886da342Sopenharmony_ci string_view token = argc < 3 ? "" : argv[2]; 317886da342Sopenharmony_ci _Exit(StartDaemon(token, argc - THREE, argv + THREE)); 318886da342Sopenharmony_ci } else if (command == "screenCap") { 319886da342Sopenharmony_ci _Exit(ScreenCap(argc, argv)); 320886da342Sopenharmony_ci } else if (command == "uiRecord") { 321886da342Sopenharmony_ci _Exit(UiRecord(argc, argv)); 322886da342Sopenharmony_ci } else if (command == "uiInput") { 323886da342Sopenharmony_ci _Exit(UiInput(argc, argv)); 324886da342Sopenharmony_ci } else if (command == "--version") { 325886da342Sopenharmony_ci PrintToConsole(VERSION); 326886da342Sopenharmony_ci _Exit(EXIT_SUCCESS); 327886da342Sopenharmony_ci } else if (command == "help") { 328886da342Sopenharmony_ci PrintToConsole(HELP_MSG); 329886da342Sopenharmony_ci _Exit(EXIT_SUCCESS); 330886da342Sopenharmony_ci } else { 331886da342Sopenharmony_ci PrintToConsole("Illegal argument: " + command); 332886da342Sopenharmony_ci PrintToConsole(HELP_MSG); 333886da342Sopenharmony_ci _Exit(EXIT_FAILURE); 334886da342Sopenharmony_ci } 335886da342Sopenharmony_ci } 336886da342Sopenharmony_ci} 337