• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &params)
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