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 28using namespace OHOS::AppExecFwk; 29 30namespace OHOS { 31namespace AAFwk { 32namespace { 33const std::string ABILITY_TOOL_NAME = "ability_tool"; 34const 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 43const 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 55const 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 63const 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 69const 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 81const std::string ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION = "error: --ability <ability-name> is expected"; 82const std::string ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION = "error: --bundle <bundle-name> is expected"; 83const std::string ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID = "error: --options windowMode <value> with invalid param"; 84const std::string ABILITY_TOOL_HELP_MSG_LACK_VALUE = "error: lack of value of key"; 85const std::string ABILITY_TOOL_HELP_MSG_ONLY_NUM = "error: current option only support number"; 86const std::string ABILITY_TOOL_HELP_LACK_OPTIONS = "error: lack of essential args"; 87 88const std::string SHORT_OPTIONS_FOR_TEST = "hb:o:p:m:w:D"; 89const 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 100const int32_t ARG_LIST_INDEX_OFFSET = 2; 101} // namespace 102 103AbilityToolCommand::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 115ErrCode 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 128ErrCode 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 137ErrCode AbilityToolCommand::init() 138{ 139 return AbilityManagerClient::GetInstance()->Connect(); 140} 141 142ErrCode AbilityToolCommand::RunAsHelpCommand() 143{ 144 resultReceiver_.append(ABILITY_TOOL_HELP_MSG); 145 return OHOS::ERR_OK; 146} 147 148ErrCode 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 174ErrCode 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 197ErrCode 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 218ErrCode 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 242ErrCode 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 360ErrCode 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 414ErrCode 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 470bool 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