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