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> &params)
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