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