/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ability_tool_command.h" #include <cstdio> #include <cstring> #include <getopt.h> #include <iostream> #include <regex> #include "ability_manager_client.h" #include "element_name.h" #include "hilog_wrapper.h" #include "bool_wrapper.h" using namespace OHOS::AppExecFwk; namespace OHOS { namespace AAFwk { namespace { const std::string ABILITY_TOOL_NAME = "ability_tool"; const std::string ABILITY_TOOL_HELP_MSG = "usage: ability_tool <command> <options>\n" "ability_tool commands list:\n" " help list available commands\n" " start start ability with options\n" " stop-service stop service with options\n" " force-stop force stop the process with bundle name\n" " test start the test framework with options\n"; const std::string ABILITY_TOOL_HELP_MSG_START = "usage: ability_tool start <options>\n" "ability_tool start options list:\n" " --help list available options\n" " --device <device-id> device Id\n" " --ability <ability-name> ability name, mandatory\n" " --bundle <bundle-name> bundle name, mandatory\n" " --options <key> <value> start options, such as windowMode 102\n" " --flags <flag> flags in a want\n" " -C cold start\n" " -D start with debug mode\n"; const std::string ABILITY_TOOL_HELP_MSG_STOP_SERVICE = "usage: ability_tool stop-service <options>\n" "ability_tool stop-service options list:\n" " --help list available options\n" " --device <device-id> device Id\n" " --ability <ability-name> ability name, mandatory\n" " --bundle <bundle-name> bundle name, mandatory\n"; const std::string ABILITY_TOOL_HELP_MSG_FORCE_STOP = "usage: ability_tool force-stop <options>\n" "ability_tool force-stop options list:\n" " --help list available options\n" " <bundle-name> bundle name, mandatory\n"; const std::string ABILITY_TOOL_HELP_MSG_TEST = "usage: ability_tool test <options>\n" "ability_tool test options list:\n" " --help list available options\n" " --bundle <bundle-name> bundle name, mandatory\n" " --options unittest <test-runner> test runner need to start, mandatory\n" " --package-name <package-name> package name, required for the FA model\n" " --module-name <module-name> module name, required for the STAGE model\n" " --options <key> <value> test options, such as testcase test_001\n" " --watchdog <wait-time> max execute time for this test\n" " -D test with debug mode\n"; const std::string ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION = "error: --ability <ability-name> is expected"; const std::string ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION = "error: --bundle <bundle-name> is expected"; const std::string ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID = "error: --options windowMode <value> with invalid param"; const std::string ABILITY_TOOL_HELP_MSG_LACK_VALUE = "error: lack of value of key"; const std::string ABILITY_TOOL_HELP_MSG_ONLY_NUM = "error: current option only support number"; const std::string ABILITY_TOOL_HELP_LACK_OPTIONS = "error: lack of essential args"; const std::string SHORT_OPTIONS_FOR_TEST = "hb:o:p:m:w:D"; const struct option LONG_OPTIONS_FOR_TEST[] = { {"help", no_argument, nullptr, 'h'}, {"bundle", required_argument, nullptr, 'b'}, {"options", required_argument, nullptr, 'o'}, {"package-name", required_argument, nullptr, 'p'}, {"module-name", required_argument, nullptr, 'm'}, {"watchdog", required_argument, nullptr, 'w'}, {"debug", no_argument, nullptr, 'D'}, {nullptr, 0, nullptr, 0}, }; const int32_t ARG_LIST_INDEX_OFFSET = 2; } // namespace AbilityToolCommand::AbilityToolCommand(int argc, char* argv[]) : ShellCommand(argc, argv, ABILITY_TOOL_NAME) { for (int i = 0; i < argc_; i++) { HILOG_INFO("argv_[%{public}d]: %{public}s", i, argv_[i]); } aaShellCmd_ = std::make_shared<AbilityManagerShellCommand>(argc, argv); if (aaShellCmd_.get() == nullptr) { HILOG_ERROR("Get aa command failed."); } } ErrCode AbilityToolCommand::CreateCommandMap() { commandMap_ = { {"help", std::bind(&AbilityToolCommand::RunAsHelpCommand, this)}, {"start", std::bind(&AbilityToolCommand::RunAsStartAbility, this)}, {"stop-service", std::bind(&AbilityToolCommand::RunAsStopService, this)}, {"force-stop", std::bind(&AbilityToolCommand::RunAsForceStop, this)}, {"test", std::bind(&AbilityToolCommand::RunAsTestCommand, this)}, }; return OHOS::ERR_OK; } ErrCode AbilityToolCommand::CreateMessageMap() { if (aaShellCmd_.get() == nullptr) { HILOG_ERROR("aa shell command is nullptr."); return OHOS::ERR_INVALID_VALUE; } return aaShellCmd_.get()->CreateMessageMap(); } ErrCode AbilityToolCommand::init() { return AbilityManagerClient::GetInstance()->Connect(); } ErrCode AbilityToolCommand::RunAsHelpCommand() { resultReceiver_.append(ABILITY_TOOL_HELP_MSG); return OHOS::ERR_OK; } ErrCode AbilityToolCommand::RunAsStartAbility() { Want want; StartOptions startoptions; ErrCode result = ParseStartAbilityArgsFromCmd(want, startoptions); if (result != OHOS::ERR_OK) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_START); return result; } result = AbilityManagerClient::GetInstance()->StartAbility(want, startoptions, nullptr); if (result != OHOS::ERR_OK) { HILOG_ERROR("%{public}s result = %{public}d", STRING_START_ABILITY_NG.c_str(), result); if (result != START_ABILITY_WAITING) { resultReceiver_ = STRING_START_ABILITY_NG + "\n"; } resultReceiver_.append(GetMessageFromCode(result)); return result; } HILOG_INFO("%{public}s", STRING_START_ABILITY_OK.c_str()); resultReceiver_ = STRING_START_ABILITY_OK + "\n"; return OHOS::ERR_OK; } ErrCode AbilityToolCommand::RunAsStopService() { Want want; ErrCode result = ParseStopServiceArgsFromCmd(want); if (result != OHOS::ERR_OK) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_STOP_SERVICE); return OHOS::ERR_INVALID_VALUE; } result = AbilityManagerClient::GetInstance()->StopServiceAbility(want); if (result != OHOS::ERR_OK) { HILOG_ERROR("%{public}s result = %{public}d", STRING_STOP_SERVICE_ABILITY_NG.c_str(), result); resultReceiver_ = STRING_STOP_SERVICE_ABILITY_NG + "\n"; resultReceiver_.append(GetMessageFromCode(result)); return result; } HILOG_INFO("%{public}s", STRING_STOP_SERVICE_ABILITY_OK.c_str()); resultReceiver_ = STRING_STOP_SERVICE_ABILITY_OK + "\n"; return OHOS::ERR_OK; } ErrCode AbilityToolCommand::RunAsForceStop() { if (argList_.empty()) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_FORCE_STOP); return OHOS::ERR_INVALID_VALUE; } std::string bundleName = argList_[0]; ErrCode result = AbilityManagerClient::GetInstance()->KillProcess(bundleName); if (result != OHOS::ERR_OK) { HILOG_ERROR("%{public}s result = %{public}d", STRING_FORCE_STOP_NG.c_str(), result); resultReceiver_ = STRING_FORCE_STOP_NG + "\n"; resultReceiver_.append(GetMessageFromCode(result)); return result; } HILOG_INFO("%{public}s", STRING_FORCE_STOP_OK.c_str()); resultReceiver_ = STRING_FORCE_STOP_OK + "\n"; return OHOS::ERR_OK; } ErrCode AbilityToolCommand::RunAsTestCommand() { std::map<std::string, std::string> params; ErrCode result = ParseTestArgsFromCmd(params); if (result != OHOS::ERR_OK) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_TEST); return result; } if (aaShellCmd_.get() == nullptr) { HILOG_ERROR("aa shell command is nullptr."); return OHOS::ERR_INVALID_VALUE; } if (!aaShellCmd_.get()->IsTestCommandIntegrity(params)) { HILOG_ERROR("test command lack of essential args."); resultReceiver_ = ABILITY_TOOL_HELP_LACK_OPTIONS + "\n"; resultReceiver_.append(ABILITY_TOOL_HELP_MSG_TEST); return OHOS::ERR_INVALID_VALUE; } return aaShellCmd_.get()->StartUserTest(params); } ErrCode AbilityToolCommand::ParseStartAbilityArgsFromCmd(Want& want, StartOptions& startoptions) { std::string deviceId = ""; std::string bundleName = ""; std::string abilityName = ""; std::string paramName = ""; std::string paramValue = ""; std::smatch sm; int32_t windowMode = AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_UNDEFINED; int flags = 0; bool isColdStart = false; bool isDebugApp = false; int option = -1; int index = 0; const std::string shortOptions = "hd:a:b:o:f:CD"; const struct option longOptions[] = { {"help", no_argument, nullptr, 'h'}, {"device", required_argument, nullptr, 'd'}, {"ability", required_argument, nullptr, 'a'}, {"bundle", required_argument, nullptr, 'b'}, {"options", required_argument, nullptr, 'o'}, {"flags", required_argument, nullptr, 'f'}, {"cold-start", no_argument, nullptr, 'C'}, {"debug", no_argument, nullptr, 'D'}, {nullptr, 0, nullptr, 0}, }; while ((option = getopt_long(argc_, argv_, shortOptions.c_str(), longOptions, &index)) != EOF) { HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind); switch (option) { case 'h': break; case 'd': deviceId = optarg; break; case 'a': abilityName = optarg; break; case 'b': bundleName = optarg; break; case 'o': if (!GetKeyAndValueByOpt(optind, paramName, paramValue)) { return OHOS::ERR_INVALID_VALUE; } HILOG_DEBUG("paramName: %{public}s, paramValue: %{public}s", paramName.c_str(), paramValue.c_str()); if (paramName == "windowMode" && std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) { windowMode = std::stoi(paramValue); } break; case 'f': paramValue = optarg; if (std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS))) { flags = std::stoi(paramValue); } break; case 'C': isColdStart = true; break; case 'D': isDebugApp = true; break; default: break; } } // Parameter check if (abilityName.size() == 0 || bundleName.size() == 0) { HILOG_DEBUG("'ability_tool %{public}s' without enough options.", cmd_.c_str()); if (abilityName.size() == 0) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n"); } if (bundleName.size() == 0) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n"); } return OHOS::ERR_INVALID_VALUE; } // Get Want ElementName element(deviceId, bundleName, abilityName); want.SetElement(element); WantParams wantParams; if (isColdStart) { wantParams.SetParam("coldStart", Boolean::Box(isColdStart)); } if (isDebugApp) { wantParams.SetParam("debugApp", Boolean::Box(isDebugApp)); } want.SetParams(wantParams); if (flags != 0) { want.AddFlags(flags); } // Get StartOptions if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_UNDEFINED) { if (windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FULLSCREEN && windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_PRIMARY && windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_SECONDARY && windowMode != AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FLOATING) { HILOG_DEBUG("'ability_tool %{public}s' %{public}s", cmd_.c_str(), ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID.c_str()); resultReceiver_.append(ABILITY_TOOL_HELP_MSG_WINDOW_MODE_INVALID + "\n"); return OHOS::ERR_INVALID_VALUE; } startoptions.SetWindowMode(windowMode); } return OHOS::ERR_OK; } ErrCode AbilityToolCommand::ParseStopServiceArgsFromCmd(Want& want) { std::string deviceId = ""; std::string abilityName = ""; std::string bundleName = ""; int option = -1; int index = 0; const std::string shortOptions = "hd:a:b:"; const struct option longOptions[] = { {"help", no_argument, nullptr, 'h'}, {"device", required_argument, nullptr, 'd'}, {"ability", required_argument, nullptr, 'a'}, {"bundle", required_argument, nullptr, 'b'}, {nullptr, 0, nullptr, 0}, }; while ((option = getopt_long(argc_, argv_, shortOptions.c_str(), longOptions, &index)) != EOF) { HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind); switch (option) { case 'h': break; case 'd': deviceId = optarg; break; case 'a': abilityName = optarg; break; case 'b': bundleName = optarg; break; default: break; } } if (abilityName.size() == 0 || bundleName.size() == 0) { HILOG_INFO("'ability_tool %{public}s' without enough options.", cmd_.c_str()); if (abilityName.size() == 0) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_ABILITY_NAME_OPTION + "\n"); } if (bundleName.size() == 0) { resultReceiver_.append(ABILITY_TOOL_HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n"); } return OHOS::ERR_INVALID_VALUE; } ElementName element(deviceId, bundleName, abilityName); want.SetElement(element); return OHOS::ERR_OK; } ErrCode AbilityToolCommand::ParseTestArgsFromCmd(std::map<std::string, std::string>& params) { std::string tempKey; std::string paramKey; std::string paramValue; std::smatch sm; int option = -1; int index = 0; // Parameter parse with conversion while ((option = getopt_long(argc_, argv_, SHORT_OPTIONS_FOR_TEST.c_str(), LONG_OPTIONS_FOR_TEST, &index)) != EOF) { HILOG_INFO("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind); switch (option) { case 'h': break; case 'b': params["-b"] = optarg; break; case 'o': if (!GetKeyAndValueByOpt(optind, tempKey, paramValue)) { return OHOS::ERR_INVALID_VALUE; } HILOG_DEBUG("tempKey: %{public}s, paramValue: %{public}s", tempKey.c_str(), paramValue.c_str()); paramKey = "-s "; paramKey.append(tempKey); params[paramKey] = paramValue; break; case 'p': params["-p"] = optarg; break; case 'm': params["-m"] = optarg; break; case 'w': paramValue = optarg; if (!(std::regex_match(paramValue, sm, std::regex(STRING_TEST_REGEX_INTEGER_NUMBERS)))) { HILOG_DEBUG("'ability_tool test --watchdog %{public}s", ABILITY_TOOL_HELP_MSG_ONLY_NUM.c_str()); resultReceiver_.append(ABILITY_TOOL_HELP_MSG_ONLY_NUM + "\n"); return OHOS::ERR_INVALID_VALUE; } params["-w"] = paramValue; break; case 'D': params["-D"] = "true"; break; default: break; } } return OHOS::ERR_OK; } bool AbilityToolCommand::GetKeyAndValueByOpt(int optind, std::string& key, std::string& value) { int argListIndex = optind - ARG_LIST_INDEX_OFFSET; if (argListIndex < 1) { return false; } bool isOption = (argList_[argListIndex - 1] == "-o" || argList_[argListIndex - 1] == "--options") ? true : false; int keyIndex = isOption ? argListIndex : argListIndex - 1; int valueIndex = isOption ? argListIndex + 1 : argListIndex; if (keyIndex >= static_cast<int>(argList_.size()) || keyIndex < 0 || valueIndex >= static_cast<int>(argList_.size()) || valueIndex < 0) { HILOG_DEBUG("'ability_tool %{public}s' %{public}s", cmd_.c_str(), ABILITY_TOOL_HELP_MSG_LACK_VALUE.c_str()); resultReceiver_.append(ABILITY_TOOL_HELP_MSG_LACK_VALUE + "\n"); return false; } key = argList_[keyIndex]; value = argList_[valueIndex]; return true; } } // namespace AAFwk } // namespace OHOS