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 "ipc_transactor.h" 37 #include "system_ui_controller.h" 38 #include "input_manager.h" 39 #include "i_input_event_consumer.h" 40 #include "pointer_event.h" 41 #include "ui_driver.h" 42 #include "ui_record.h" 43 44 using namespace std; 45 using namespace std::chrono; 46 47 namespace OHOS::uitest { 48 const std::string HELP_MSG = 49 " help, print help messages\n" 50 " screenCap, \n" 51 " dumpLayout, \n" 52 " uiRecord -record, wirte location coordinates of events into files\n" 53 " uiRecord -read, print file content to the console\n" 54 " --version, print current tool version\n"; 55 const std::string VERSION = "3.2.5.0"; 56 struct option g_longoptions[] = { 57 {"save file in this path", required_argument, nullptr, 'p'}, 58 {"dump all UI trees in json array format", no_argument, nullptr, 'I'} 59 }; 60 /* *Print to the console of this shell process. */ PrintToConsole(string_view message)61 static inline void PrintToConsole(string_view message) 62 { 63 std::cout << message << std::endl; 64 } 65 GetParam(int32_t argc,char * argv[],string_view optstring,string_view usage,map<char,string> & params)66 static int32_t GetParam(int32_t argc, char *argv[], string_view optstring, string_view usage, 67 map<char, string> ¶ms) 68 { 69 int opt; 70 while ((opt = getopt_long(argc, argv, optstring.data(), g_longoptions, nullptr)) != -1) { 71 switch (opt) { 72 case '?': 73 PrintToConsole(usage); 74 return EXIT_FAILURE; 75 case 'i': 76 params.insert(pair<char, string>(opt, "true")); 77 break; 78 default: 79 params.insert(pair<char, string>(opt, optarg)); 80 break; 81 } 82 } 83 return EXIT_SUCCESS; 84 } 85 DumpLayout(int32_t argc,char * argv[])86 static int32_t DumpLayout(int32_t argc, char *argv[]) 87 { 88 auto ts = to_string(GetCurrentMicroseconds()); 89 auto savePath = "/data/local/tmp/layout_" + ts + ".json"; 90 map<char, string> params; 91 static constexpr string_view usage = "USAGE: uitestkit dumpLayout -p <path>"; 92 if (GetParam(argc, argv, "p:i", usage, params) == EXIT_FAILURE) { 93 return EXIT_FAILURE; 94 } 95 auto iter = params.find('p'); 96 if (iter != params.end()) { 97 savePath = iter->second; 98 } 99 ofstream fout; 100 fout.open(savePath, ios::out | ios::binary); 101 if (!fout) { 102 PrintToConsole("Error path:" + savePath); 103 return EXIT_FAILURE; 104 } 105 auto controller = make_unique<SysUiController>("sys_ui_controller"); 106 if (!controller->ConnectToSysAbility()) { 107 PrintToConsole("Dump layout failed, cannot connect to AAMS"); 108 fout.close(); 109 return EXIT_FAILURE; 110 } 111 if (params.find('i') != params.end()) { 112 vector<pair<Window, nlohmann::json>> datas; 113 controller->GetUiHierarchy(datas); 114 auto array = nlohmann::json::array(); 115 for (auto& data : datas) { 116 array.push_back(data.second); 117 } 118 fout << array.dump(); 119 } else { 120 UiController::RegisterController(move(controller), Priority::MEDIUM); 121 auto data = nlohmann::json(); 122 auto driver = UiDriver(); 123 auto error = ApiCallErr(NO_ERROR); 124 driver.DumpUiHierarchy(data, error); 125 if (error.code_ != NO_ERROR) { 126 PrintToConsole("Dump layout failed: " + error.message_); 127 fout.close(); 128 return EXIT_FAILURE; 129 } 130 fout << data.dump(); 131 } 132 PrintToConsole("DumpLayout saved to:" + savePath); 133 fout.close(); 134 return EXIT_SUCCESS; 135 } 136 ScreenCap(int32_t argc,char * argv[])137 static int32_t ScreenCap(int32_t argc, char *argv[]) 138 { 139 auto ts = to_string(GetCurrentMicroseconds()); 140 auto savePath = "/data/local/tmp/screenCap_" + ts + ".png"; 141 map<char, string> params; 142 static constexpr string_view usage = "USAGE: uitest screenCap -p <path>"; 143 if (GetParam(argc, argv, "p:", usage, params) == EXIT_FAILURE) { 144 return EXIT_FAILURE; 145 } 146 auto iter = params.find('p'); 147 if (iter != params.end()) { 148 savePath = iter->second; 149 } 150 auto controller = SysUiController("sys_ui_controller"); 151 stringstream errorRecv; 152 if (!controller.TakeScreenCap(savePath, errorRecv)) { 153 PrintToConsole("ScreenCap failed: " + errorRecv.str()); 154 return EXIT_FAILURE; 155 } 156 PrintToConsole("ScreenCap saved to " + savePath); 157 return EXIT_SUCCESS; 158 } 159 TranslateToken(string_view raw)160 static string TranslateToken(string_view raw) 161 { 162 if (raw.find_first_of('@') != string_view::npos) { 163 return string(raw); 164 } 165 return "default"; 166 } 167 StartDaemon(string_view token)168 static int32_t StartDaemon(string_view token) 169 { 170 if (token.empty()) { 171 LOG_E("Empty transaction token"); 172 return EXIT_FAILURE; 173 } 174 auto transalatedToken = TranslateToken(token); 175 if (daemon(0, 0) != 0) { 176 LOG_E("Failed to daemonize current process"); 177 return EXIT_FAILURE; 178 } 179 LOG_I("Server starting up"); 180 ApiTransactor apiTransactServer(true); 181 if (!apiTransactServer.InitAndConnectPeer(transalatedToken, ApiTransact)) { 182 LOG_E("Failed to initialize server"); 183 return EXIT_FAILURE; 184 } 185 // set delayed UiController 186 auto controllerProvider = [](list<unique_ptr<UiController>> &receiver) { 187 auto controller = make_unique<SysUiController>("sys_ui_controller"); 188 if (controller->ConnectToSysAbility()) { 189 receiver.push_back(move(controller)); 190 } 191 }; 192 UiController::RegisterControllerProvider(controllerProvider); 193 mutex mtx; 194 unique_lock<mutex> lock(mtx); 195 condition_variable condVar; 196 apiTransactServer.SetDeathCallback([&condVar]() { 197 condVar.notify_one(); 198 }); 199 LOG_I("UiTest-daemon running, pid=%{public}d", getpid()); 200 condVar.wait(lock); 201 LOG_I("Server exit"); 202 apiTransactServer.Finalize(); 203 _Exit(0); 204 return 0; 205 } 206 UiRecord(int32_t argc,char * argv[])207 static int32_t UiRecord(int32_t argc, char *argv[]) 208 { 209 static constexpr string_view usage = "USAGE: uitest uiRecord <read|record>"; 210 if (!(argc == INDEX_THREE || argc == INDEX_FOUR)) { 211 PrintToConsole(usage); 212 return EXIT_FAILURE; 213 } 214 std::string opt = argv[TWO]; 215 std::string modeOpt; 216 if (argc == INDEX_FOUR) { 217 modeOpt = argv[THREE]; 218 } 219 if (opt == "record") { 220 if (!InitEventRecordFile()) { 221 return OHOS::ERR_INVALID_VALUE; 222 } 223 auto controller = make_unique<SysUiController>("sys_ui_controller"); 224 if (!controller->ConnectToSysAbility()) { 225 PrintToConsole("Failed, cannot connect to AMMS "); 226 return EXIT_FAILURE; 227 } 228 UiController::RegisterController(move(controller), Priority::MEDIUM); 229 RecordInitEnv(modeOpt); 230 auto callBackPtr = InputEventCallback::GetPtr(); 231 if (callBackPtr == nullptr) { 232 std::cout << "nullptr" << std::endl; 233 return OHOS::ERR_INVALID_VALUE; 234 } 235 int32_t id1 = MMI::InputManager::GetInstance()->AddMonitor(callBackPtr); 236 if (id1 == -1) { 237 std::cout << "Startup Failed!" << std::endl; 238 return OHOS::ERR_INVALID_VALUE; 239 } 240 Timer timer; 241 timer.Start(TIMEINTERVAL, Timer::TimerFunc); 242 std::cout << "Started Recording Successfully..." << std::endl; 243 int flag = getc(stdin); 244 std::cout << flag << std::endl; 245 constexpr int timeToSleep = 3600; 246 sleep(timeToSleep); 247 return OHOS::ERR_OK; 248 } else if (opt == "read") { 249 EventData::ReadEventLine(); 250 return OHOS::ERR_OK; 251 } else { 252 PrintToConsole(usage); 253 return EXIT_FAILURE; 254 } 255 } 256 main(int32_t argc,char * argv[])257 extern "C" int32_t main(int32_t argc, char *argv[]) 258 { 259 static constexpr string_view usage = "USAGE: uitest <help|screenCap|dumpLayout|uiRecord|--version>"; 260 if ((size_t)argc < INDEX_TWO) { 261 PrintToConsole("Missing argument"); 262 PrintToConsole(usage); 263 exit(EXIT_FAILURE); 264 } 265 string command(argv[1]); 266 if (command == "dumpLayout") { 267 exit(DumpLayout(argc, argv)); 268 } else if (command == "start-daemon") { 269 string_view token = argc < 3 ? "" : argv[2]; 270 exit(StartDaemon(token)); 271 } else if (command == "screenCap") { 272 exit(ScreenCap(argc, argv)); 273 } else if (command == "uiRecord") { 274 exit(UiRecord(argc, argv)); 275 } else if (command == "--version") { 276 PrintToConsole(VERSION); 277 exit(EXIT_SUCCESS); 278 } else if (command == "help") { 279 PrintToConsole(HELP_MSG); 280 exit(EXIT_SUCCESS); 281 } else { 282 PrintToConsole("Illegal argument: " + command); 283 PrintToConsole(usage); 284 exit(EXIT_FAILURE); 285 } 286 } 287 } 288