1 /*
2 * Copyright (c) 2022-2024 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 "bool_wrapper.h"
25 #include "element_name.h"
26 #include "hilog_tag_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 TAG_LOGI(AAFwkTag::AA_TOOL, "argv_[%{public}d]: %{public}s", i, argv_[i]);
107 }
108
109 aaShellCmd_ = std::make_shared<AbilityManagerShellCommand>(argc, argv);
110 if (aaShellCmd_.get() == nullptr) {
111 TAG_LOGE(AAFwkTag::AA_TOOL, "Get aa command failed");
112 }
113 }
114
CreateCommandMap()115 ErrCode AbilityToolCommand::CreateCommandMap()
116 {
117 commandMap_ = {
118 {"help", [this]() { return this->RunAsHelpCommand(); }},
119 {"start", [this]() { return this->RunAsStartAbility(); }},
120 {"stop-service", [this]() { return this->RunAsStopService(); }},
121 {"force-stop", [this]() { return this->RunAsForceStop(); }},
122 {"test", [this]() { return this->RunAsTestCommand(); }},
123 };
124
125 return OHOS::ERR_OK;
126 }
127
CreateMessageMap()128 ErrCode AbilityToolCommand::CreateMessageMap()
129 {
130 if (aaShellCmd_.get() == nullptr) {
131 TAG_LOGE(AAFwkTag::AA_TOOL, "null aaShellCmd_");
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 TAG_LOGE(AAFwkTag::AA_TOOL, "%{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 TAG_LOGI(AAFwkTag::AA_TOOL, "%{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 TAG_LOGE(AAFwkTag::AA_TOOL, "%{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 TAG_LOGI(AAFwkTag::AA_TOOL, "%{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 TAG_LOGE(AAFwkTag::AA_TOOL, "%{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 TAG_LOGI(AAFwkTag::AA_TOOL, "%{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 TAG_LOGE(AAFwkTag::AA_TOOL, "null aaShellCmd_");
230 return OHOS::ERR_INVALID_VALUE;
231 }
232
233 if (!aaShellCmd_.get()->IsTestCommandIntegrity(params)) {
234 TAG_LOGE(AAFwkTag::AA_TOOL, "invalid params");
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 TAG_LOGI(
271 AAFwkTag::AA_TOOL, "option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
272 switch (option) {
273 case 'h':
274 break;
275 case 'd':
276 deviceId = optarg;
277 break;
278 case 'a':
279 abilityName = optarg;
280 break;
281 case 'b':
282 bundleName = optarg;
283 break;
284 case 'o':
285 if (!GetKeyAndValueByOpt(optind, paramName, paramValue)) {
286 return OHOS::ERR_INVALID_VALUE;
287 }
288 TAG_LOGD(AAFwkTag::AA_TOOL, "paramName: %{public}s, paramValue: %{public}s", paramName.c_str(),
289 paramValue.c_str());
290 if (paramName == "windowMode" &&
291 std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) {
292 windowMode = std::stoi(paramValue);
293 }
294 break;
295 case 'f':
296 paramValue = optarg;
297 if (std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) {
298 flags = std::stoi(paramValue);
299 }
300 break;
301 case 'C':
302 isColdStart = true;
303 break;
304 case 'D':
305 isDebugApp = true;
306 break;
307 default:
308 break;
309 }
310 }
311
312 // Parameter check
313 if (abilityName.size() == 0 || bundleName.size() == 0) {
314 TAG_LOGD(AAFwkTag::AA_TOOL, "'ability_tool %{public}s' without enough options", cmd_.c_str());
315 if (abilityName.size() == 0) {
316 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n");
317 }
318
319 if (bundleName.size() == 0) {
320 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n");
321 }
322
323 return OHOS::ERR_INVALID_VALUE;
324 }
325
326 // Get Want
327 ElementName element(deviceId, bundleName, abilityName);
328 want.SetElement(element);
329
330 WantParams wantParams;
331 if (isColdStart) {
332 wantParams.SetParam("coldStart", Boolean::Box(isColdStart));
333 }
334 if (isDebugApp) {
335 wantParams.SetParam("debugApp", Boolean::Box(isDebugApp));
336 }
337 want.SetParams(wantParams);
338
339 if (flags != 0) {
340 want.AddFlags(flags);
341 }
342
343 // Get StartOptions
344 if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_UNDEFINED) {
345 if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FULLSCREEN &&
346 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_PRIMARY &&
347 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_SECONDARY &&
348 windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FLOATING) {
349 TAG_LOGD(AAFwkTag::AA_TOOL, "'ability_tool %{public}s' %{public}s", cmd_.c_str(),
350 ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID.c_str());
351 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID + "\n");
352 return OHOS::ERR_INVALID_VALUE;
353 }
354 startOptions.SetWindowMode(windowMode);
355 }
356
357 return OHOS::ERR_OK;
358 }
359
ParseStopServiceArgsFromCmd(Want & want)360 ErrCode AbilityToolCommand::ParseStopServiceArgsFromCmd(Want& want)
361 {
362 std::string deviceId = "";
363 std::string abilityName = "";
364 std::string bundleName = "";
365 int option = -1;
366 int index = 0;
367 const std::string shortOptions = "hd:a:b:";
368 const struct option longOptions[] = {
369 {"help", no_argument, nullptr, 'h'},
370 {"device", required_argument, nullptr, 'd'},
371 {"ability", required_argument, nullptr, 'a'},
372 {"bundle", required_argument, nullptr, 'b'},
373 {nullptr, 0, nullptr, 0},
374 };
375
376 while ((option = getopt_long(argc_, argv_, shortOptions.c_str(), longOptions, &index)) != EOF) {
377 TAG_LOGI(
378 AAFwkTag::AA_TOOL, "option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
379 switch (option) {
380 case 'h':
381 break;
382 case 'd':
383 deviceId = optarg;
384 break;
385 case 'a':
386 abilityName = optarg;
387 break;
388 case 'b':
389 bundleName = optarg;
390 break;
391 default:
392 break;
393 }
394 }
395
396 if (abilityName.size() == 0 || bundleName.size() == 0) {
397 TAG_LOGI(AAFwkTag::AA_TOOL, "'ability_tool %{public}s' without enough options", cmd_.c_str());
398 if (abilityName.size() == 0) {
399 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n");
400 }
401
402 if (bundleName.size() == 0) {
403 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n");
404 }
405
406 return OHOS::ERR_INVALID_VALUE;
407 }
408
409 ElementName element(deviceId, bundleName, abilityName);
410 want.SetElement(element);
411 return OHOS::ERR_OK;
412 }
413
ParseTestArgsFromCmd(std::map<std::string,std::string> & params)414 ErrCode AbilityToolCommand::ParseTestArgsFromCmd(std::map<std::string, std::string>& params)
415 {
416 std::string tempKey;
417 std::string paramKey;
418 std::string paramValue;
419 std::smatch sm;
420 int option = -1;
421 int index = 0;
422
423 // Parameter parse with conversion
424 while ((option = getopt_long(argc_, argv_, SHORT_OPTIONS_FOR_TEST.c_str(), LONG_OPTIONS_FOR_TEST, &index)) != EOF) {
425 TAG_LOGI(
426 AAFwkTag::AA_TOOL, "option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
427 switch (option) {
428 case 'h':
429 break;
430 case 'b':
431 params["-b"] = optarg;
432 break;
433 case 'o':
434 if (!GetKeyAndValueByOpt(optind, tempKey, paramValue)) {
435 return OHOS::ERR_INVALID_VALUE;
436 }
437 TAG_LOGD(AAFwkTag::AA_TOOL, "tempKey: %{public}s, paramValue: %{public}s", tempKey.c_str(),
438 paramValue.c_str());
439 paramKey = "-s ";
440 paramKey.append(tempKey);
441 params[paramKey] = paramValue;
442 break;
443 case 'p':
444 params["-p"] = optarg;
445 break;
446 case 'm':
447 params["-m"] = optarg;
448 break;
449 case 'w':
450 paramValue = optarg;
451 if (!(std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS)))) {
452 TAG_LOGD(AAFwkTag::AA_TOOL, "'ability_tool test --watchdog %{public}s",
453 ABILITY_TOOL_HELP_MSG_ONLY_NUM.c_str());
454 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_ONLY_NUM + "\n");
455 return OHOS::ERR_INVALID_VALUE;
456 }
457 params["-w"] = paramValue;
458 break;
459 case 'D':
460 params["-D"] = "true";
461 break;
462 default:
463 break;
464 }
465 }
466
467 return OHOS::ERR_OK;
468 }
469
GetKeyAndValueByOpt(int optind,std::string & key,std::string & value)470 bool AbilityToolCommand::GetKeyAndValueByOpt(int optind, std::string& key, std::string& value)
471 {
472 int argListIndex = optind - ARG_LIST_INDEX_OFFSET;
473 if (argListIndex < 1) {
474 return false;
475 }
476
477 bool isOption = (argList_[argListIndex - 1] == "-o" || argList_[argListIndex - 1] == "--options") ? true : false;
478 int keyIndex = isOption ? argListIndex : argListIndex - 1;
479 int valueIndex = isOption ? argListIndex + 1 : argListIndex;
480 if (keyIndex >= static_cast<int>(argList_.size()) || keyIndex < 0 ||
481 valueIndex >= static_cast<int>(argList_.size()) || valueIndex < 0) {
482 TAG_LOGD(AAFwkTag::AA_TOOL, "'ability_tool %{public}s' %{public}s", cmd_.c_str(),
483 ABILITY_TOOL_HELP_MSG_LACK_VALUE.c_str());
484 resultReceiver_.append(ABILITY_TOOL_HELP_MSG_LACK_VALUE + "\n");
485 return false;
486 }
487
488 key = argList_[keyIndex];
489 value = argList_[valueIndex];
490 return true;
491 }
492 } // namespace AAFwk
493 } // namespace OHOS
494