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