1 /*
2 * Copyright (c) 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 #include "ability_tool_command.h"
16
17 #include <cstdio>
18 #include <cstring>
19 #include <getopt.h>
20 #include <iostream>
21 #include <regex>
22
23 #include "ability_manager_client.h"
24 #include "element_name.h"
25 #include "hilog_wrapper.h"
26 #include "bool_wrapper.h"
27
28 using namespace OHOS::AppExecFwk;
29
30 namespace OHOS {
31 namespace AAFwk {
32 namespace {
33 const std::string ABILITY_TOOL_NAME = "ability_tool";
34 const std::string ABILITY_TOOL_HELP_MSG =
35 "usage: ability_tool <command> <options>\n"
36 "ability_tool commands list:\n"
37 " help list available commands\n"
38 " start start ability with options\n"
39 " stop-service stop service with options\n"
40 " force-stop force stop the process with bundle name\n"
41 " test start the test framework with options\n";
42
43 const std::string ABILITY_TOOL_HELP_MSG_START =
44 "usage: ability_tool start <options>\n"
45 "ability_tool start options list:\n"
46 " --help list available options\n"
47 " --device <device-id> device Id\n"
48 " --ability <ability-name> ability name, mandatory\n"
49 " --bundle <bundle-name> bundle name, mandatory\n"
50 " --options <key> <value> start options, such as windowMode 102\n"
51 " --flags <flag> flags in a want\n"
52 " -C cold start\n"
53 " -D start with debug mode\n";
54
55 const std::string ABILITY_TOOL_HELP_MSG_STOP_SERVICE =
56 "usage: ability_tool stop-service <options>\n"
57 "ability_tool stop-service options list:\n"
58 " --help list available options\n"
59 " --device <device-id> device Id\n"
60 " --ability <ability-name> ability name, mandatory\n"
61 " --bundle <bundle-name> bundle name, mandatory\n";
62
63 const std::string ABILITY_TOOL_HELP_MSG_FORCE_STOP =
64 "usage: ability_tool force-stop <options>\n"
65 "ability_tool force-stop options list:\n"
66 " --help list available options\n"
67 " <bundle-name> bundle name, mandatory\n";
68
69 const std::string ABILITY_TOOL_HELP_MSG_TEST =
70 "usage: ability_tool test <options>\n"
71 "ability_tool test options list:\n"
72 " --help list available options\n"
73 " --bundle <bundle-name> bundle name, mandatory\n"
74 " --options unittest <test-runner> test runner need to start, mandatory\n"
75 " --package-name <package-name> package name, required for the FA model\n"
76 " --module-name <module-name> module name, required for the STAGE model\n"
77 " --options <key> <value> test options, such as testcase test_001\n"
78 " --watchdog <wait-time> max execute time for this test\n"
79 " -D test with debug mode\n";
80
81 const std::string ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION = "error: --ability <ability-name> is expected";
82 const std::string ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION = "error: --bundle <bundle-name> is expected";
83 const std::string ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID = "error: --options windowMode <value> with invalid param";
84 const std::string ABILITY_TOOL_HELP_MSG_LACK_VALUE = "error: lack of value of key";
85 const std::string ABILITY_TOOL_HELP_MSG_ONLY_NUM = "error: current option only support number";
86 const std::string ABILITY_TOOL_HELP_LACK_OPTIONS = "error: lack of essential args";
87
88 const std::string SHORT_OPTIONS_FOR_TEST = "hb:o:p:m:w:D";
89 const struct option LONG_OPTIONS_FOR_TEST[] = {
90 {"help", no_argument, nullptr, 'h'},
91 {"bundle", required_argument, nullptr, 'b'},
92 {"options", required_argument, nullptr, 'o'},
93 {"package-name", required_argument, nullptr, 'p'},
94 {"module-name", required_argument, nullptr, 'm'},
95 {"watchdog", required_argument, nullptr, 'w'},
96 {"debug", no_argument, nullptr, 'D'},
97 {nullptr, 0, nullptr, 0},
98 };
99
100 const int32_t ARG_LIST_INDEX_OFFSET = 2;
101 } // namespace
102
AbilityToolCommand(int argc,char * argv[])103 AbilityToolCommand::AbilityToolCommand(int argc, char* argv[]) : ShellCommand(argc, argv, ABILITY_TOOL_NAME)
104 {
105 for (int i = 0; i < argc_; i++) {
106 HILOG_INFO("argv_[%{public}d]: %{public}s", i, argv_[i]);
107 }
108
109 aaShellCmd_ = std::make_shared<AbilityManagerShellCommand>(argc, argv);
110 if (aaShellCmd_.get() == nullptr) {
111 HILOG_ERROR("Get aa command failed.");
112 }
113 }
114
CreateCommandMap()115 ErrCode AbilityToolCommand::CreateCommandMap()
116 {
117 commandMap_ = {
118 {"help", std::bind(&AbilityToolCommand::RunAsHelpCommand, this)},
119 {"start", std::bind(&AbilityToolCommand::RunAsStartAbility, this)},
120 {"stop-service", std::bind(&AbilityToolCommand::RunAsStopService, this)},
121 {"force-stop", std::bind(&AbilityToolCommand::RunAsForceStop, this)},
122 {"test", std::bind(&AbilityToolCommand::RunAsTestCommand, this)},
123 };
124
125 return OHOS::ERR_OK;
126 }
127
CreateMessageMap()128 ErrCode AbilityToolCommand::CreateMessageMap()
129 {
130 if (aaShellCmd_.get() == nullptr) {
131 HILOG_ERROR("aa shell command is nullptr.");
132 return OHOS::ERR_INVALID_VALUE;
133 }
134 return aaShellCmd_.get()->CreateMessageMap();
135 }
136
init()137 ErrCode AbilityToolCommand::init()
138 {
139 return AbilityManagerClient::GetInstance()->Connect();
140 }
141
RunAsHelpCommand()142 ErrCode AbilityToolCommand::RunAsHelpCommand()
143 {
144 resultReceiver_.append(ABILITY_TOOL_HELP_MSG);
145 return OHOS::ERR_OK;
146 }
147
RunAsStartAbility()148 ErrCode AbilityToolCommand::RunAsStartAbility()
149 {
150 Want want;
151 StartOptions startoptions;
152
153 ErrCode result = ParseStartAbilityArgsFromCmd(want, startoptions);
154 if (result != OHOS::ERR_OK) {
155 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_START);
156 return result;
157 }
158
159 result = AbilityManagerClient::GetInstance()->StartAbility(want, startoptions, nullptr);
160 if (result != OHOS::ERR_OK) {
161 HILOG_ERROR("%{public}s result = %{public}d", STRING_START_ABILITY_NG.c_str(), result);
162 if (result != START_ABILITY_WAITING) {
163 resultReceiver_ = STRING_START_ABILITY_NG + "\n";
164 }
165 resultReceiver_.append(GetMessageFromCode(result));
166 return result;
167 }
168
169 HILOG_INFO("%{public}s", STRING_START_ABILITY_OK.c_str());
170 resultReceiver_ = STRING_START_ABILITY_OK + "\n";
171 return OHOS::ERR_OK;
172 }
173
RunAsStopService()174 ErrCode AbilityToolCommand::RunAsStopService()
175 {
176 Want want;
177
178 ErrCode result = ParseStopServiceArgsFromCmd(want);
179 if (result != OHOS::ERR_OK) {
180 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_STOP_SERVICE);
181 return OHOS::ERR_INVALID_VALUE;
182 }
183
184 result = AbilityManagerClient::GetInstance()->StopServiceAbility(want);
185 if (result != OHOS::ERR_OK) {
186 HILOG_ERROR("%{public}s result = %{public}d", STRING_STOP_SERVICE_ABILITY_NG.c_str(), result);
187 resultReceiver_ = STRING_STOP_SERVICE_ABILITY_NG + "\n";
188 resultReceiver_.append(GetMessageFromCode(result));
189 return result;
190 }
191
192 HILOG_INFO("%{public}s", STRING_STOP_SERVICE_ABILITY_OK.c_str());
193 resultReceiver_ = STRING_STOP_SERVICE_ABILITY_OK + "\n";
194 return OHOS::ERR_OK;
195 }
196
RunAsForceStop()197 ErrCode AbilityToolCommand::RunAsForceStop()
198 {
199 if (argList_.empty()) {
200 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_FORCE_STOP);
201 return OHOS::ERR_INVALID_VALUE;
202 }
203
204 std::string bundleName = argList_[0];
205 ErrCode result = AbilityManagerClient::GetInstance()->KillProcess(bundleName);
206 if (result != OHOS::ERR_OK) {
207 HILOG_ERROR("%{public}s result = %{public}d", STRING_FORCE_STOP_NG.c_str(), result);
208 resultReceiver_ = STRING_FORCE_STOP_NG + "\n";
209 resultReceiver_.append(GetMessageFromCode(result));
210 return result;
211 }
212
213 HILOG_INFO("%{public}s", STRING_FORCE_STOP_OK.c_str());
214 resultReceiver_ = STRING_FORCE_STOP_OK + "\n";
215 return OHOS::ERR_OK;
216 }
217
RunAsTestCommand()218 ErrCode AbilityToolCommand::RunAsTestCommand()
219 {
220 std::map<std::string, std::string> params;
221
222 ErrCode result = ParseTestArgsFromCmd(params);
223 if (result != OHOS::ERR_OK) {
224 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_TEST);
225 return result;
226 }
227
228 if (aaShellCmd_.get() == nullptr) {
229 HILOG_ERROR("aa shell command is nullptr.");
230 return OHOS::ERR_INVALID_VALUE;
231 }
232
233 if (!aaShellCmd_.get()->IsTestCommandIntegrity(params)) {
234 HILOG_ERROR("test command lack of essential args.");
235 resultReceiver_ = ABILITY_TOOL_HELP_LACK_OPTIONS + "\n";
236 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_TEST);
237 return OHOS::ERR_INVALID_VALUE;
238 }
239 return aaShellCmd_.get()->StartUserTest(params);
240 }
241
ParseStartAbilityArgsFromCmd(Want & want,StartOptions & startoptions)242 ErrCode AbilityToolCommand::ParseStartAbilityArgsFromCmd(Want& want, StartOptions& startoptions)
243 {
244 std::string deviceId = "";
245 std::string bundleName = "";
246 std::string abilityName = "";
247 std::string paramName = "";
248 std::string paramValue = "";
249 std::smatch sm;
250 int32_t windowMode = AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_UNDEFINED;
251 int flags = 0;
252 bool isColdStart = false;
253 bool isDebugApp = false;
254 int option = -1;
255 int index = 0;
256 const std::string shortOptions = "hd:a:b:o:f:CD";
257 const struct option longOptions[] = {
258 {"help", no_argument, nullptr, 'h'},
259 {"device", required_argument, nullptr, 'd'},
260 {"ability", required_argument, nullptr, 'a'},
261 {"bundle", required_argument, nullptr, 'b'},
262 {"options", required_argument, nullptr, 'o'},
263 {"flags", required_argument, nullptr, 'f'},
264 {"cold-start", no_argument, nullptr, 'C'},
265 {"debug", no_argument, nullptr, 'D'},
266 {nullptr, 0, nullptr, 0},
267 };
268
269 while ((option = getopt_long(argc_, argv_, shortOptions.c_str(), longOptions, &index)) != EOF) {
270 HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
271 switch (option) {
272 case 'h':
273 break;
274 case 'd':
275 deviceId = optarg;
276 break;
277 case 'a':
278 abilityName = optarg;
279 break;
280 case 'b':
281 bundleName = optarg;
282 break;
283 case 'o':
284 if (!GetKeyAndValueByOpt(optind, paramName, paramValue)) {
285 return OHOS::ERR_INVALID_VALUE;
286 }
287 HILOG_DEBUG("paramName: %{public}s, paramValue: %{public}s", paramName.c_str(), paramValue.c_str());
288 if (paramName == "windowMode" &&
289 std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) {
290 windowMode = std::stoi(paramValue);
291 }
292 break;
293 case 'f':
294 paramValue = optarg;
295 if (std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) {
296 flags = std::stoi(paramValue);
297 }
298 break;
299 case 'C':
300 isColdStart = true;
301 break;
302 case 'D':
303 isDebugApp = true;
304 break;
305 default:
306 break;
307 }
308 }
309
310 // Parameter check
311 if (abilityName.size() == 0 || bundleName.size() == 0) {
312 HILOG_DEBUG("'ability_tool %{public}s' without enough options.", cmd_.c_str());
313 if (abilityName.size() == 0) {
314 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n");
315 }
316
317 if (bundleName.size() == 0) {
318 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n");
319 }
320
321 return OHOS::ERR_INVALID_VALUE;
322 }
323
324 // Get Want
325 ElementName element(deviceId, bundleName, abilityName);
326 want.SetElement(element);
327
328 WantParams wantParams;
329 if (isColdStart) {
330 wantParams.SetParam("coldStart", Boolean::Box(isColdStart));
331 }
332 if (isDebugApp) {
333 wantParams.SetParam("debugApp", Boolean::Box(isDebugApp));
334 }
335 want.SetParams(wantParams);
336
337 if (flags != 0) {
338 want.AddFlags(flags);
339 }
340
341 // Get StartOptions
342 if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_UNDEFINED) {
343 if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FULLSCREEN &&
344 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_PRIMARY &&
345 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_SECONDARY &&
346 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FLOATING) {
347 HILOG_DEBUG("'ability_tool %{public}s' %{public}s", cmd_.c_str(),
348 ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID.c_str());
349 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID + "\n");
350 return OHOS::ERR_INVALID_VALUE;
351 }
352 startoptions.SetWindowMode(windowMode);
353 }
354
355 return OHOS::ERR_OK;
356 }
357
ParseStopServiceArgsFromCmd(Want & want)358 ErrCode AbilityToolCommand::ParseStopServiceArgsFromCmd(Want& want)
359 {
360 std::string deviceId = "";
361 std::string abilityName = "";
362 std::string bundleName = "";
363 int option = -1;
364 int index = 0;
365 const std::string shortOptions = "hd:a:b:";
366 const struct option longOptions[] = {
367 {"help", no_argument, nullptr, 'h'},
368 {"device", required_argument, nullptr, 'd'},
369 {"ability", required_argument, nullptr, 'a'},
370 {"bundle", required_argument, nullptr, 'b'},
371 {nullptr, 0, nullptr, 0},
372 };
373
374 while ((option = getopt_long(argc_, argv_, shortOptions.c_str(), longOptions, &index)) != EOF) {
375 HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
376 switch (option) {
377 case 'h':
378 break;
379 case 'd':
380 deviceId = optarg;
381 break;
382 case 'a':
383 abilityName = optarg;
384 break;
385 case 'b':
386 bundleName = optarg;
387 break;
388 default:
389 break;
390 }
391 }
392
393 if (abilityName.size() == 0 || bundleName.size() == 0) {
394 HILOG_INFO("'ability_tool %{public}s' without enough options.", cmd_.c_str());
395 if (abilityName.size() == 0) {
396 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n");
397 }
398
399 if (bundleName.size() == 0) {
400 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n");
401 }
402
403 return OHOS::ERR_INVALID_VALUE;
404 }
405
406 ElementName element(deviceId, bundleName, abilityName);
407 want.SetElement(element);
408 return OHOS::ERR_OK;
409 }
410
ParseTestArgsFromCmd(std::map<std::string,std::string> & params)411 ErrCode AbilityToolCommand::ParseTestArgsFromCmd(std::map<std::string, std::string>& params)
412 {
413 std::string tempKey;
414 std::string paramKey;
415 std::string paramValue;
416 std::smatch sm;
417 int option = -1;
418 int index = 0;
419
420 // Parameter parse with conversion
421 while ((option = getopt_long(argc_, argv_, SHORT_OPTIONS_FOR_TEST.c_str(), LONG_OPTIONS_FOR_TEST, &index)) != EOF) {
422 HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
423 switch (option) {
424 case 'h':
425 break;
426 case 'b':
427 params["-b"] = optarg;
428 break;
429 case 'o':
430 if (!GetKeyAndValueByOpt(optind, tempKey, paramValue)) {
431 return OHOS::ERR_INVALID_VALUE;
432 }
433 HILOG_DEBUG("tempKey: %{public}s, paramValue: %{public}s", tempKey.c_str(), paramValue.c_str());
434 paramKey = "-s ";
435 paramKey.append(tempKey);
436 params[paramKey] = paramValue;
437 break;
438 case 'p':
439 params["-p"] = optarg;
440 break;
441 case 'm':
442 params["-m"] = optarg;
443 break;
444 case 'w':
445 paramValue = optarg;
446 if (!(std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS)))) {
447 HILOG_DEBUG("'ability_tool test --watchdog %{public}s", ABILITY_TOOL_HELP_MSG_ONLY_NUM.c_str());
448 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_ONLY_NUM + "\n");
449 return OHOS::ERR_INVALID_VALUE;
450 }
451 params["-w"] = paramValue;
452 break;
453 case 'D':
454 params["-D"] = "true";
455 break;
456 default:
457 break;
458 }
459 }
460
461 return OHOS::ERR_OK;
462 }
463
GetKeyAndValueByOpt(int optind,std::string & key,std::string & value)464 bool AbilityToolCommand::GetKeyAndValueByOpt(int optind, std::string& key, std::string& value)
465 {
466 int argListIndex = optind - ARG_LIST_INDEX_OFFSET;
467 if (argListIndex < 1) {
468 return false;
469 }
470
471 bool isOption = (argList_[argListIndex - 1] == "-o" || argList_[argListIndex - 1] == "--options") ? true : false;
472 int keyIndex = isOption ? argListIndex : argListIndex - 1;
473 int valueIndex = isOption ? argListIndex + 1 : argListIndex;
474 if (keyIndex >= static_cast<int>(argList_.size()) || keyIndex < 0 ||
475 valueIndex >= static_cast<int>(argList_.size()) || valueIndex < 0) {
476 HILOG_DEBUG("'ability_tool %{public}s' %{public}s", cmd_.c_str(),
477 ABILITY_TOOL_HELP_MSG_LACK_VALUE.c_str());
478 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_LACK_VALUE + "\n");
479 return false;
480 }
481
482 key = argList_[keyIndex];
483 value = argList_[valueIndex];
484 return true;
485 }
486 } // namespace AAFwk
487 } // namespace OHOS
488