/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipc_transactor.h" #include "system_ui_controller.h" #include "input_manager.h" #include "i_input_event_consumer.h" #include "pointer_event.h" #include "ui_driver.h" #include "ui_record.h" #include "ui_input.h" #include "ui_model.h" #include "extension_executor.h" using namespace std; using namespace std::chrono; namespace OHOS::uitest { const std::string HELP_MSG = " help, print help messages\n" " screenCap, \n" " dumpLayout, \n" " uiRecord record, wirte location coordinates of events into files\n" " uiRecord read, print file content to the console\n" " uiAction input, \n" " --version, print current tool version\n"; const std::string VERSION = "4.1.4.5"; struct option g_longoptions[] = { {"save file in this path", required_argument, nullptr, 'p'}, {"dump all UI trees in json array format", no_argument, nullptr, 'I'} }; /* *Print to the console of this shell process. */ static inline void PrintToConsole(string_view message) { std::cout << message << std::endl; } static int32_t GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, map ¶ms) { int opt; while ((opt = getopt_long(argc, argv, optstring.data(), g_longoptions, nullptr)) != -1) { switch (opt) { case '?': PrintToConsole(usage); return EXIT_FAILURE; case 'i': params.insert(pair(opt, "true")); break; case 'a': params.insert(pair(opt, "true")); break; default: params.insert(pair(opt, optarg)); break; } } return EXIT_SUCCESS; } static void DumpLayoutImpl(string_view path, bool listWindows, bool initController, bool addExternAttr, ApiCallErr &err) { ofstream fout; fout.open(path, ios::out | ios::binary); if (!fout) { err = ApiCallErr(ERR_INVALID_INPUT, "Error path:" + string(path) + strerror(errno)); return; } if (initController) { UiDriver::RegisterController(make_unique()); } auto driver = UiDriver(); auto data = nlohmann::json(); driver.DumpUiHierarchy(data, listWindows, addExternAttr, err); if (err.code_ != NO_ERROR) { fout.close(); return; } fout << data.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); fout.close(); return; } static int32_t DumpLayout(int32_t argc, char *argv[]) { auto ts = to_string(GetCurrentMicroseconds()); auto savePath = "/data/local/tmp/layout_" + ts + ".json"; map params; static constexpr string_view usage = "USAGE: uitestkit dumpLayout -p "; if (GetParam(argc, argv, "p:ia", usage, params) == EXIT_FAILURE) { return EXIT_FAILURE; } auto iter = params.find('p'); if (iter != params.end()) { savePath = iter->second; } const bool listWindows = params.find('i') != params.end(); const bool addExternAttr = params.find('a') != params.end(); auto err = ApiCallErr(NO_ERROR); DumpLayoutImpl(savePath, listWindows, true, addExternAttr, err); if (err.code_ == NO_ERROR) { PrintToConsole("DumpLayout saved to:" + savePath); return EXIT_SUCCESS; } else if (err.code_ != ERR_INITIALIZE_FAILED) { PrintToConsole("DumpLayout failed:" + err.message_); return EXIT_FAILURE; } // Cannot connect to AAMS, broadcast request to running uitest-daemon if any err = ApiCallErr(NO_ERROR); auto cmd = OHOS::AAFwk::Want(); cmd.SetParam("savePath", string(savePath)); cmd.SetParam("listWindows", listWindows); ApiTransactor::SendBroadcastCommand(cmd, err); if (err.code_ == NO_ERROR) { PrintToConsole("DumpLayout saved to:" + savePath); return EXIT_SUCCESS; } else { PrintToConsole("DumpLayout failed:" + err.message_); return EXIT_FAILURE; } } static int32_t ScreenCap(int32_t argc, char *argv[]) { auto ts = to_string(GetCurrentMicroseconds()); auto savePath = "/data/local/tmp/screenCap_" + ts + ".png"; map params; static constexpr string_view usage = "USAGE: uitest screenCap -p "; if (GetParam(argc, argv, "p:", usage, params) == EXIT_FAILURE) { return EXIT_FAILURE; } auto iter = params.find('p'); if (iter != params.end()) { savePath = iter->second; } auto controller = SysUiController(); stringstream errorRecv; auto fd = open(savePath.c_str(), O_RDWR | O_CREAT, 0666); if (!controller.TakeScreenCap(fd, errorRecv)) { PrintToConsole("ScreenCap failed: " + errorRecv.str()); return EXIT_FAILURE; } PrintToConsole("ScreenCap saved to " + savePath); (void) close(fd); return EXIT_SUCCESS; } static string TranslateToken(string_view raw) { if (raw.find_first_of('@') != string_view::npos) { return string(raw); } return "default"; } static int32_t StartDaemon(string_view token) { if (token.empty()) { LOG_E("Empty transaction token"); return EXIT_FAILURE; } auto transalatedToken = TranslateToken(token); if (daemon(0, 0) != 0) { LOG_E("Failed to daemonize current process"); return EXIT_FAILURE; } LOG_I("Server starting up"); UiDriver::RegisterController(make_unique()); // accept remopte dump request during deamon running (initController=false) ApiTransactor::SetBroadcastCommandHandler([] (const OHOS::AAFwk::Want &cmd, ApiCallErr &err) { DumpLayoutImpl(cmd.GetStringParam("savePath"), cmd.GetBoolParam("listWindows", false), false, false, err); }); if (token == "singleness") { ExecuteExtension(VERSION); LOG_I("Server exit"); ApiTransactor::UnsetBroadcastCommandHandler(); _Exit(0); return 0; } ApiTransactor apiTransactServer(true); auto &apiServer = FrontendApiServer::Get(); auto apiHandler = std::bind(&FrontendApiServer::Call, &apiServer, placeholders::_1, placeholders::_2); auto cbHandler = std::bind(&ApiTransactor::Transact, &apiTransactServer, placeholders::_1, placeholders::_2); apiServer.SetCallbackHandler(cbHandler); // used for callback from server to client if (!apiTransactServer.InitAndConnectPeer(transalatedToken, apiHandler)) { LOG_E("Failed to initialize server"); ApiTransactor::UnsetBroadcastCommandHandler(); _Exit(0); return EXIT_FAILURE; } mutex mtx; unique_lock lock(mtx); condition_variable condVar; apiTransactServer.SetDeathCallback([&condVar]() { condVar.notify_one(); }); LOG_I("UiTest-daemon running, pid=%{public}d", getpid()); condVar.wait(lock); LOG_I("Server exit"); apiTransactServer.Finalize(); ApiTransactor::UnsetBroadcastCommandHandler(); _Exit(0); return 0; } static int32_t UiRecord(int32_t argc, char *argv[]) { static constexpr string_view usage = "USAGE: uitest uiRecord "; if (!(argc == INDEX_THREE || argc == INDEX_FOUR)) { PrintToConsole(usage); return EXIT_FAILURE; } std::string opt = argv[TWO]; std::string modeOpt; if (argc == INDEX_FOUR) { modeOpt = argv[THREE]; } if (opt == "record") { auto controller = make_unique(); if (!controller->ConnectToSysAbility()) { PrintToConsole("Failed, cannot connect to AMMS "); return EXIT_FAILURE; } UiDriver::RegisterController(move(controller)); return UiDriverRecordStart(modeOpt); } else if (opt == "read") { EventData::ReadEventLine(); return OHOS::ERR_OK; } else { PrintToConsole(usage); return EXIT_FAILURE; } } static int32_t UiInput(int32_t argc, char *argv[]) { if ((size_t)argc < INDEX_FOUR) { PrintInputMessage(); return EXIT_FAILURE; } if ((string)argv[THREE] == "help") { PrintInputMessage(); return OHOS::ERR_OK; } auto controller = make_unique(); if (!controller->ConnectToSysAbility()) { PrintToConsole("Failed, cannot connect to AMMS "); return EXIT_FAILURE; } UiDriver::RegisterController(move(controller)); return UiActionInput(argc, argv); } extern "C" int32_t main(int32_t argc, char *argv[]) { static constexpr string_view usage = "USAGE: uitest "; if ((size_t)argc < INDEX_TWO) { PrintToConsole("Missing argument"); PrintToConsole(usage); exit(EXIT_FAILURE); } string command(argv[1]); if (command == "dumpLayout") { _Exit(DumpLayout(argc, argv)); } else if (command == "start-daemon") { string_view token = argc < 3 ? "" : argv[2]; exit(StartDaemon(token)); } else if (command == "screenCap") { exit(ScreenCap(argc, argv)); } else if (command == "uiRecord") { exit(UiRecord(argc, argv)); } else if (command == "uiInput") { exit(UiInput(argc, argv)); } else if (command == "--version") { PrintToConsole(VERSION); exit(EXIT_SUCCESS); } else if (command == "help") { PrintToConsole(HELP_MSG); exit(EXIT_SUCCESS); } else { PrintToConsole("Illegal argument: " + command); PrintToConsole(usage); exit(EXIT_FAILURE); } } }