mirror of
https://github.com/openharmony/ability_ability_runtime.git
synced 2026-08-24 12:43:16 -04:00
Merge branch 'dispatcher-0429' of git@gitcode.com:xialiangwei/ability_ability_runtime.git into 'master'
# Conflicts: # conflict interfaces/kits/c/ability_runtime/ability_runtime_common.h Co-Authored-By:Agent
This commit is contained in:
@@ -258,4 +258,10 @@ declare_args() {
|
||||
!defined(global_parts_info.distributeddatamgr_udmf)) {
|
||||
ability_runtime_udmf_enable = false
|
||||
}
|
||||
|
||||
hiviewdfx_runtime_api_metrics_enable = true
|
||||
if (defined(global_parts_info) &&
|
||||
!defined(global_parts_info.hiviewdfx_api_metrics)) {
|
||||
hiviewdfx_runtime_api_metrics_enable = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ interface OHOS.AAFwk.IAbilityConnection;
|
||||
rawdata AgentCard..OHOS.AgentRuntime.AgentCardsRawData;
|
||||
interface OHOS.AgentRuntime.IAgentManager {
|
||||
void GetAllAgentCards([out] AgentCardsRawData cards);
|
||||
void GetAgentCardsByBundleName([in] String bundleName, [out] AgentCard[] cards);
|
||||
void GetAgentCardsByBundleName([in] String bundleName, [out] AgentCardsRawData cards);
|
||||
void GetAgentCardByAgentId([in] String bundleName, [in] String agentId, [out] AgentCard card);
|
||||
void GetCallerAgentCardByAgentId([in] String agentId, [out] AgentCard card);
|
||||
void RegisterAgentCard([in] AgentCard card);
|
||||
|
||||
@@ -62,7 +62,13 @@ int32_t AgentManagerClient::GetAgentCardsByBundleName(const std::string &bundleN
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "null agentmgr");
|
||||
return ERR_NULL_AGENT_MGR_PROXY;
|
||||
}
|
||||
return agentMgr->GetAgentCardsByBundleName(bundleName, cards);
|
||||
AgentCardsRawData rawData;
|
||||
auto ret = agentMgr->GetAgentCardsByBundleName(bundleName, rawData);
|
||||
if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "get by bundle failed: %{public}d", ret);
|
||||
return ret;
|
||||
}
|
||||
return AgentCardsRawData::ToAgentCardVec(rawData, cards);
|
||||
}
|
||||
|
||||
int32_t AgentManagerClient::GetAgentCardByAgentId(const std::string &bundleName, const std::string &agentId,
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
private:
|
||||
OHOS::AppExecFwk::BundleMgrClient bundleMgrClient_;
|
||||
mutable std::mutex cardDataMutex_;
|
||||
AgentCardMgr();
|
||||
~AgentCardMgr();
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
|
||||
int32_t GetAllAgentCards(AgentCardsRawData &cards) override;
|
||||
|
||||
int32_t GetAgentCardsByBundleName(const std::string &bundleName, std::vector<AgentCard> &cards) override;
|
||||
int32_t GetAgentCardsByBundleName(const std::string &bundleName, AgentCardsRawData &cards) override;
|
||||
|
||||
int32_t GetAgentCardByAgentId(const std::string &bundleName, const std::string &agentId, AgentCard &card) override;
|
||||
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
void Init();
|
||||
void RegisterBundleEventCallback();
|
||||
/**
|
||||
* @brief Validates caller permission and reserves one slot from the per-caller connection quota.
|
||||
* @brief Validates caller permission and foreground state before classifying the agent connect request.
|
||||
*/
|
||||
int32_t ValidateConnectAgentRequest(const sptr<AAFwk::IAbilityConnection> &connection, int32_t &callerUid);
|
||||
/**
|
||||
@@ -153,6 +153,8 @@ private:
|
||||
bool ReleaseCallerConnectionCountLocked(const sptr<IRemoteObject> &callerRemote);
|
||||
void ReleaseTrackedConnection(const sptr<AAFwk::IAbilityConnection> &connection);
|
||||
void ReleaseTrackedConnectionByRemoteLocked(const sptr<IRemoteObject> &callerRemote);
|
||||
void TransferLowCodeCallerLimitLocked(const std::shared_ptr<AgentHostSession> &session,
|
||||
const sptr<IRemoteObject> &callerRemote);
|
||||
void HandleCallerConnectionDied(const wptr<IRemoteObject> &remote);
|
||||
void HandleCallerConnectionDied(const sptr<IRemoteObject> &remote);
|
||||
/**
|
||||
|
||||
@@ -158,6 +158,7 @@ int32_t AgentCardMgr::HandleBundleInstall(const std::string &bundleName, int32_t
|
||||
}
|
||||
|
||||
std::vector<StoredAgentCardEntry> storedEntries;
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
int32_t ret = AgentCardDbMgr::GetInstance().QueryData(bundleName, userId, storedEntries);
|
||||
if (ret != ERR_OK && ret != ERR_NAME_NOT_FOUND) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "query stored cards failed: %{public}d", ret);
|
||||
@@ -198,6 +199,7 @@ int32_t AgentCardMgr::HandleBundleRemove(const std::string &bundleName, int32_t
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "invalid bundleName");
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
return AgentCardDbMgr::GetInstance().DeleteData(bundleName, userId);
|
||||
}
|
||||
|
||||
@@ -213,6 +215,7 @@ int32_t AgentCardMgr::GetAgentCardsByBundleName(const std::string &bundleName, s
|
||||
{
|
||||
int32_t userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE;
|
||||
std::vector<StoredAgentCardEntry> entries;
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
int32_t ret = AgentCardDbMgr::GetInstance().QueryData(bundleName, userId, entries);
|
||||
if (ret == ERR_OK) {
|
||||
cards = ExtractCards(entries);
|
||||
@@ -275,6 +278,7 @@ int32_t AgentCardMgr::RegisterAgentCard(const AgentCard &card)
|
||||
}
|
||||
|
||||
std::vector<StoredAgentCardEntry> entries;
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
int32_t ret = AgentCardDbMgr::GetInstance().QueryData(registerCard.appInfo->bundleName, userId, entries);
|
||||
if (ret != ERR_OK && ret != ERR_NAME_NOT_FOUND) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "query data failed: %{public}d", ret);
|
||||
@@ -288,6 +292,10 @@ int32_t AgentCardMgr::RegisterAgentCard(const AgentCard &card)
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "agent card already registered");
|
||||
return AAFwk::ERR_AGENT_CARD_DUPLICATE_REGISTER;
|
||||
}
|
||||
if (entries.size() >= MAX_AGENT_CARD_SIZE) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "agent card count reached max size %{public}d", MAX_AGENT_CARD_SIZE);
|
||||
return AAFwk::ERR_AGENT_CARD_LIST_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
entries.push_back({registerCard, AgentCardUpdateSource::API});
|
||||
return AgentCardDbMgr::GetInstance().InsertData(registerCard.appInfo->bundleName, userId, entries);
|
||||
@@ -316,6 +324,7 @@ int32_t AgentCardMgr::UpdateAgentCard(const AgentCard &card)
|
||||
}
|
||||
|
||||
std::vector<StoredAgentCardEntry> entries;
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
int32_t ret = AgentCardDbMgr::GetInstance().QueryData(card.appInfo->bundleName, userId, entries);
|
||||
if (ret == ERR_NAME_NOT_FOUND) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "bundle cards not found");
|
||||
@@ -369,6 +378,7 @@ int32_t AgentCardMgr::DeleteAgentCard(const std::string &bundleName, const std::
|
||||
|
||||
int32_t userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE;
|
||||
std::vector<StoredAgentCardEntry> bundleCards;
|
||||
std::lock_guard<std::mutex> lock(cardDataMutex_);
|
||||
int32_t ret = AgentCardDbMgr::GetInstance().QueryData(bundleName, userId, bundleCards);
|
||||
if (ret == ERR_NAME_NOT_FOUND) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "bundle cards not found");
|
||||
|
||||
@@ -175,7 +175,7 @@ int32_t AgentManagerService::GetAllAgentCards(AgentCardsRawData &cards)
|
||||
return AgentCardMgr::GetInstance().GetAllAgentCards(cards);
|
||||
}
|
||||
|
||||
int32_t AgentManagerService::GetAgentCardsByBundleName(const std::string &bundleName, std::vector<AgentCard> &cards)
|
||||
int32_t AgentManagerService::GetAgentCardsByBundleName(const std::string &bundleName, AgentCardsRawData &cards)
|
||||
{
|
||||
if (!AAFwk::PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPI()) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "caller no system-app, can not use system-api");
|
||||
@@ -186,7 +186,8 @@ int32_t AgentManagerService::GetAgentCardsByBundleName(const std::string &bundle
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "Permission verification failed");
|
||||
return ERR_PERMISSION_DENIED;
|
||||
}
|
||||
auto ret = AgentCardMgr::GetInstance().GetAgentCardsByBundleName(bundleName, cards);
|
||||
std::vector<AgentCard> cardVec;
|
||||
auto ret = AgentCardMgr::GetInstance().GetAgentCardsByBundleName(bundleName, cardVec);
|
||||
if (ret == ERR_NAME_NOT_FOUND) {
|
||||
TAG_LOGW(AAFwkTag::SER_ROUTER, "no agent cards of bundle %{public}s", bundleName.c_str());
|
||||
int32_t userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE;
|
||||
@@ -198,8 +199,12 @@ int32_t AgentManagerService::GetAgentCardsByBundleName(const std::string &bundle
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "bundle unexist");
|
||||
return AAFwk::ERR_BUNDLE_NOT_EXIST;
|
||||
}
|
||||
AgentCardsRawData::FromAgentCardVec({}, cards);
|
||||
return ERR_OK;
|
||||
}
|
||||
if (ret == ERR_OK) {
|
||||
AgentCardsRawData::FromAgentCardVec(cardVec, cards);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -306,7 +311,7 @@ int32_t AgentManagerService::DeleteAgentCard(const std::string &bundleName, cons
|
||||
int32_t AgentManagerService::ConnectAgentExtensionAbility(const AAFwk::Want &want,
|
||||
const sptr<AAFwk::IAbilityConnection> &connection)
|
||||
{
|
||||
// Step 1: validate caller state and shared caller-side connection quota.
|
||||
// Step 1: validate caller state before classifying the agent connect request.
|
||||
int32_t callerUid = 0;
|
||||
auto ret = ValidateConnectAgentRequest(connection, callerUid);
|
||||
if (ret != ERR_OK) {
|
||||
@@ -409,15 +414,7 @@ int32_t AgentManagerService::ValidateConnectAgentRequest(const sptr<AAFwk::IAbil
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
// Reserve only against the caller-level shared connection budget.
|
||||
callerUid = IPCSkeleton::GetCallingUid();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionLock_);
|
||||
if (HasReachedCallerConnectionLimitLocked(callerUid)) {
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "Maximum agent connections reached for callerUid: %{public}d", callerUid);
|
||||
return AAFwk::ERR_MAX_AGENT_CONNECTIONS_REACHED;
|
||||
}
|
||||
}
|
||||
|
||||
// Only foreground apps are allowed to initiate agent connects.
|
||||
auto callerPid = IPCSkeleton::GetCallingPid();
|
||||
@@ -922,6 +919,7 @@ void AgentManagerService::ReleaseTrackedConnectionByRemoteLocked(const sptr<IRem
|
||||
}
|
||||
|
||||
auto callerUid = it->second.callerUid;
|
||||
auto countTowardsCallerLimit = it->second.countTowardsCallerLimit;
|
||||
if (it->second.callerRemote != nullptr && it->second.deathRecipient != nullptr) {
|
||||
it->second.callerRemote->RemoveDeathRecipient(it->second.deathRecipient);
|
||||
}
|
||||
@@ -931,6 +929,9 @@ void AgentManagerService::ReleaseTrackedConnectionByRemoteLocked(const sptr<IRem
|
||||
if (isDisconnecting) {
|
||||
return;
|
||||
}
|
||||
if (!countTowardsCallerLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto countIt = callerConnectionCounts_.find(callerUid);
|
||||
if (countIt == callerConnectionCounts_.end()) {
|
||||
@@ -943,6 +944,35 @@ void AgentManagerService::ReleaseTrackedConnectionByRemoteLocked(const sptr<IRem
|
||||
countIt->second--;
|
||||
}
|
||||
|
||||
void AgentManagerService::TransferLowCodeCallerLimitLocked(const std::shared_ptr<AgentHostSession> &session,
|
||||
const sptr<IRemoteObject> &callerRemote)
|
||||
{
|
||||
if (session == nullptr || callerRemote == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto currentIter = trackedConnections_.find(callerRemote);
|
||||
if (currentIter == trackedConnections_.end() || !currentIter->second.countTowardsCallerLimit) {
|
||||
return;
|
||||
}
|
||||
for (const auto &connectionEntry : session->callerConnections) {
|
||||
const auto &candidateRemote = connectionEntry.first;
|
||||
if (candidateRemote == nullptr || candidateRemote == callerRemote) {
|
||||
continue;
|
||||
}
|
||||
auto candidateIter = trackedConnections_.find(candidateRemote);
|
||||
if (candidateIter == trackedConnections_.end() || !candidateIter->second.isLowCode ||
|
||||
candidateIter->second.hostKey < currentIter->second.hostKey ||
|
||||
currentIter->second.hostKey < candidateIter->second.hostKey ||
|
||||
candidateIter->second.callerUid != currentIter->second.callerUid ||
|
||||
candidateIter->second.countTowardsCallerLimit) {
|
||||
continue;
|
||||
}
|
||||
candidateIter->second.countTowardsCallerLimit = true;
|
||||
currentIter->second.countTowardsCallerLimit = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AgentManagerService::HandleCallerConnectionDied(const sptr<IRemoteObject> &remote)
|
||||
{
|
||||
sptr<AAFwk::IAbilityConnection> serviceConnection = nullptr;
|
||||
@@ -976,6 +1006,8 @@ void AgentManagerService::HandleCallerConnectionDied(const sptr<IRemoteObject> &
|
||||
if (!session->isDisconnecting && session->agents.empty()) {
|
||||
session->isDisconnecting = true;
|
||||
hostConnection = session->hostConnection;
|
||||
} else {
|
||||
TransferLowCodeCallerLimitLocked(session, remote);
|
||||
}
|
||||
}
|
||||
ReleaseTrackedConnectionByRemoteLocked(remote);
|
||||
@@ -1071,6 +1103,7 @@ int32_t AgentManagerService::NotifyLowCodeAgentComplete(const std::string &agent
|
||||
agentOwners_.erase(ownerIter);
|
||||
if (!callerStillOwnsAgent && callerRemote != nullptr) {
|
||||
session->callerConnections.erase(callerRemote);
|
||||
TransferLowCodeCallerLimitLocked(session, callerRemote);
|
||||
ReleaseTrackedConnectionByRemoteLocked(callerRemote);
|
||||
}
|
||||
if (!session->agents.empty() || session->isDisconnecting) {
|
||||
@@ -1170,7 +1203,8 @@ int32_t AgentManagerService::PrepareLowCodeConnectPlan(const AgentHostKey &hostK
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = TryRegisterConnectionLocked(connection, callingUid, session->hostConnection, &hostKey);
|
||||
auto ret = TryRegisterConnectionLocked(connection, callingUid, session->hostConnection, &hostKey,
|
||||
plan.needRealConnect);
|
||||
if (ret != ERR_OK) {
|
||||
if (plan.needRealConnect) {
|
||||
agentHostSessions_.erase(hostKey);
|
||||
@@ -1214,6 +1248,8 @@ void AgentManagerService::CleanupLowCodeConnectPlan(const AgentConnectPlan &plan
|
||||
}
|
||||
if (session->callerConnections.empty() && session->agents.empty()) {
|
||||
agentHostSessions_.erase(sessionIter);
|
||||
} else {
|
||||
TransferLowCodeCallerLimitLocked(session, plan.callerRemote);
|
||||
}
|
||||
}
|
||||
if (plan.registeredTrackedConnection && plan.callerRemote != nullptr) {
|
||||
|
||||
+13
-3
@@ -51,6 +51,7 @@
|
||||
"accessibility",
|
||||
"access_token",
|
||||
"ace_engine",
|
||||
"api_metrics",
|
||||
"app_domain_verify",
|
||||
"app_file_service",
|
||||
"appspawn",
|
||||
@@ -88,6 +89,7 @@
|
||||
"ipc",
|
||||
"json",
|
||||
"kv_store",
|
||||
"libjpeg-turbo",
|
||||
"libuv",
|
||||
"libxml2",
|
||||
"media_library",
|
||||
@@ -118,9 +120,6 @@
|
||||
"zlib",
|
||||
"hiperf",
|
||||
"hiprofiler"
|
||||
],
|
||||
"third_party": [
|
||||
"libjpeg-turbo"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
@@ -853,6 +852,16 @@
|
||||
},
|
||||
"name": "//foundation/ability/ability_runtime/frameworks/ets/ani/featureAbility:featureability_ani"
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"header_base": "//foundation/ability/ability_runtime/frameworks/native/ability/native/ability_runtime/madvise",
|
||||
"header_files": [
|
||||
"madvise_utils.h",
|
||||
"vma_utils.h"
|
||||
]
|
||||
},
|
||||
"name": "//foundation/ability/ability_runtime/frameworks/native/ability/native/ability_runtime/madvise:ability_madvise"
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"header_base": "//foundation/ability/ability_runtime/agent_runtime_framework/interfaces/kits/native/agent_extension/connection/include/",
|
||||
@@ -879,6 +888,7 @@
|
||||
}
|
||||
],
|
||||
"test": [
|
||||
"//foundation/ability/ability_runtime/cli_tool_framework/test/unittest:unittest",
|
||||
"//foundation/ability/ability_runtime/test/moduletest:moduletest",
|
||||
"//foundation/ability/ability_runtime/test/fuzztest:fuzztest",
|
||||
"//foundation/ability/ability_runtime/test/unittest:unittest",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/cj_environment"
|
||||
|
||||
@@ -20,10 +21,9 @@ ohos_unittest("cj_environment_test") {
|
||||
sources = [ "cj_environment_test.cpp" ]
|
||||
sources += [ "cj_invoker.h" ]
|
||||
|
||||
deps = []
|
||||
deps = [ "${ability_runtime_path}/cj_environment/frameworks/cj_environment:cj_environment" ]
|
||||
|
||||
external_deps = [
|
||||
"ability_runtime:cj_environment",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
|
||||
@@ -14,12 +14,22 @@
|
||||
"uid" : "aimgr",
|
||||
"gid" : ["system"],
|
||||
"ondemand" : true,
|
||||
"cgroup" : true,
|
||||
"caps" : ["KILL"],
|
||||
"secon" : "u:r:aimgr:s0",
|
||||
"jobs" : {
|
||||
"on-start" : "services:aimgr"
|
||||
},
|
||||
"permission" : [
|
||||
"ohos.permission.GET_BUNDLE_INFO_PRIVILEGED"
|
||||
"ohos.permission.GET_BUNDLE_INFO_PRIVILEGED",
|
||||
"ohos.permission.MANAGE_TOOL_TOKENID",
|
||||
"ohos.permission.RUNNING_STATE_OBSERVER",
|
||||
"ohos.permission.MANAGE_SKILL_PRIVILEGE",
|
||||
"ohos.permission.PARENT_CONTROL_UI",
|
||||
"ohos.permission.START_ABILITIES_FROM_BACKGROUND"
|
||||
],
|
||||
"permission_acls" : [
|
||||
"ohos.permission.MANAGE_TOOL_TOKENID"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -17,5 +17,7 @@ import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
group("cli_tool_framework_packages") {
|
||||
deps = [
|
||||
"${cli_tool_framework_path}/frameworks/js/napi/cli_tool_manager:climanager_napi",
|
||||
"${cli_tool_framework_path}/frameworks/js/napi/skill_driver:skilldriver_napi",
|
||||
"${cli_tool_framework_path}/frameworks/js/napi/script_manager:scriptmanager_napi",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,61 @@
|
||||
import("//build/ohos.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
ohos_source_set("cli_manager_error_utils_src") {
|
||||
sanitize = {
|
||||
cfi = true
|
||||
cfi_cross_dso = true
|
||||
debug = false
|
||||
}
|
||||
|
||||
include_dirs = [
|
||||
"include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
]
|
||||
|
||||
sources = [ "src/cli_manager_error_utils.cpp" ]
|
||||
|
||||
deps = [
|
||||
"${ability_runtime_innerkits_path}/ability_manager:ability_manager",
|
||||
"${ability_runtime_innerkits_path}/error_utils:ability_runtime_error_util",
|
||||
"${ability_runtime_napi_path}/inner/napi_common:napi_common",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"hilog:libhilog",
|
||||
"napi:ace_napi",
|
||||
]
|
||||
|
||||
subsystem_name = "ability"
|
||||
part_name = "ability_runtime"
|
||||
}
|
||||
|
||||
ohos_source_set("js_cli_event_handler_manager_src") {
|
||||
sanitize = {
|
||||
cfi = true
|
||||
cfi_cross_dso = true
|
||||
debug = false
|
||||
}
|
||||
|
||||
include_dirs = [
|
||||
"include",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
]
|
||||
|
||||
sources = [ "src/js_cli_event_handler_manager.cpp" ]
|
||||
|
||||
external_deps = [
|
||||
"c_utils:utils",
|
||||
"eventhandler:libeventhandler",
|
||||
"hilog:libhilog",
|
||||
]
|
||||
|
||||
subsystem_name = "ability"
|
||||
part_name = "ability_runtime"
|
||||
}
|
||||
|
||||
ohos_shared_library("climanager_napi") {
|
||||
sanitize = {
|
||||
cfi = true
|
||||
@@ -28,11 +83,9 @@ ohos_shared_library("climanager_napi") {
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/cli_manager_error_utils.cpp",
|
||||
"src/cli_tool_manager_module.cpp",
|
||||
"src/js_cli_manager.cpp",
|
||||
"src/js_cli_manager_utils.cpp",
|
||||
"src/js_cli_event_handler_manager.cpp",
|
||||
"src/js_cli_session_event_callback.cpp",
|
||||
]
|
||||
|
||||
@@ -41,6 +94,8 @@ ohos_shared_library("climanager_napi") {
|
||||
"${ability_runtime_innerkits_path}/runtime:runtime",
|
||||
"${ability_runtime_napi_path}/inner/napi_common:napi_common",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client",
|
||||
":cli_manager_error_utils_src",
|
||||
":js_cli_event_handler_manager_src",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
@@ -52,7 +107,7 @@ ohos_shared_library("climanager_napi") {
|
||||
"napi:ace_napi",
|
||||
]
|
||||
|
||||
relative_install_dir = "module/app/ability"
|
||||
relative_install_dir = "module/app/cli"
|
||||
subsystem_name = "ability"
|
||||
part_name = "ability_runtime"
|
||||
}
|
||||
|
||||
+1
-9
@@ -19,7 +19,6 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "arg_mapping.h"
|
||||
#include "native_engine/native_engine.h"
|
||||
#include "tool_info.h"
|
||||
#include "tool_summary.h"
|
||||
@@ -29,6 +28,7 @@ namespace CliTool {
|
||||
class CliSessionInfo;
|
||||
class CliToolEvent;
|
||||
class ExecOptions;
|
||||
|
||||
/**
|
||||
* @brief Unwrap a string map from JavaScript object.
|
||||
* @param env The N-API environment.
|
||||
@@ -66,14 +66,6 @@ bool IsValidToolEventCallback(napi_env env, napi_value obj);
|
||||
*/
|
||||
napi_value CreateJsCliToolEvent(napi_env env, const CliToolEvent &event);
|
||||
|
||||
/**
|
||||
* @brief Create JavaScript ArgMapping object.
|
||||
* @param env The N-API environment.
|
||||
* @param argMapping The ArgMapping structure.
|
||||
* @return Returns the JavaScript object.
|
||||
*/
|
||||
napi_value CreateJsArgMapping(napi_env env, const ArgMapping &argMapping);
|
||||
|
||||
/**
|
||||
* @brief Create JavaScript SubCommandInfo object.
|
||||
* @param env The N-API environment.
|
||||
|
||||
+2
-2
@@ -20,10 +20,10 @@ static napi_module _module = {
|
||||
.nm_version = 0,
|
||||
.nm_filename = "app/cli_tool/climanager_napi.so/cli_manager.js",
|
||||
.nm_register_func = OHOS::CliTool::JSCliManagerInit,
|
||||
.nm_modname = "app.ability.cliManager",
|
||||
.nm_modname = "app.cli.cliManager",
|
||||
};
|
||||
|
||||
extern "C" __attribute__((constructor)) void NAPI_app_ability_CliManager_AutoRegister(void)
|
||||
extern "C" __attribute__((constructor)) void NAPI_app_cli_CliManager_AutoRegister(void)
|
||||
{
|
||||
napi_module_register(&_module);
|
||||
}
|
||||
|
||||
@@ -15,23 +15,21 @@
|
||||
|
||||
#include "js_cli_manager.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "cli_manager_error_utils.h"
|
||||
#include "cli_session_info.h"
|
||||
#include "cli_tool_mgr_client.h"
|
||||
#include "exec_tool_callback_impl.h"
|
||||
#include "exec_result.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "js_cli_event_handler_manager.h"
|
||||
#include "js_cli_manager_utils.h"
|
||||
#include "js_cli_session_event_callback.h"
|
||||
#include "js_error_utils.h"
|
||||
#include "napi_common_util.h"
|
||||
#include "napi_common_want.h"
|
||||
|
||||
#include "js_cli_event_handler_manager.h"
|
||||
#include "js_cli_session_event_callback.h"
|
||||
|
||||
using namespace OHOS::AbilityRuntime;
|
||||
|
||||
namespace OHOS {
|
||||
@@ -42,6 +40,29 @@ constexpr int32_t INDEX_ONE = 1;
|
||||
constexpr int32_t INDEX_TWO = 2;
|
||||
constexpr int32_t INDEX_THREE = 3;
|
||||
constexpr int32_t INDEX_FOUR = 4;
|
||||
|
||||
int32_t DispatchCliTool(const ExecToolParam ¶m, napi_env env,
|
||||
std::shared_ptr<NapiAsyncTask> asyncTask)
|
||||
{
|
||||
CliToolMGRClient::ExecToolReplyCallback replyCallback =
|
||||
[env, asyncTask](int32_t resultCode, const CliSessionInfo &session) {
|
||||
JsCliEventHandlerManager::GetInstance().PostTask(
|
||||
[env, asyncTask, resultCode, session]() {
|
||||
HandleScope handleScope(env);
|
||||
if (resultCode != ERR_OK) {
|
||||
asyncTask->Reject(env, CreateCliJsErrorByNativeErr(env, resultCode));
|
||||
return;
|
||||
}
|
||||
napi_value jsSession = CreateJsCliSessionInfo(env, session);
|
||||
if (jsSession == nullptr) {
|
||||
asyncTask->Reject(env, CreateJsUndefined(env));
|
||||
return;
|
||||
}
|
||||
asyncTask->ResolveWithNoError(env, jsSession);
|
||||
});
|
||||
};
|
||||
return CliToolMGRClient::GetInstance().ExecTool(param, replyCallback);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void JSCliManager::Finalizer(napi_env env, void *data, void *hint)
|
||||
@@ -115,7 +136,7 @@ napi_value JSCliManager::OnExecTool(napi_env env, size_t argc, napi_value *argv)
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
|
||||
if (!AppExecFwk::UnwrapStringFromJS2(env, argv[INDEX_THREE], param.challenge)) {
|
||||
if (!AppExecFwk::UnwrapStringFromJS2(env, argv[INDEX_THREE], param.challenge) || param.challenge.empty()) {
|
||||
ThrowInvalidParamError(env, "Tool challenge is required");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
@@ -131,24 +152,7 @@ napi_value JSCliManager::OnExecTool(napi_env env, size_t argc, napi_value *argv)
|
||||
auto uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
|
||||
std::shared_ptr<NapiAsyncTask> asyncTask = std::move(uasyncTask);
|
||||
|
||||
// Create callback task that will be invoked when ExecTool completes
|
||||
CliToolMGRClient::ExecToolReplyCallback replyCallback =
|
||||
[env, asyncTask](int32_t resultCode, const CliSessionInfo &session) {
|
||||
JsCliEventHandlerManager::GetInstance().PostTask([env, asyncTask, resultCode, session]() {
|
||||
HandleScope handleScope(env);
|
||||
if (resultCode != ERR_OK) {
|
||||
asyncTask->Reject(env, CreateCliJsErrorByNativeErr(env, resultCode));
|
||||
return;
|
||||
}
|
||||
napi_value jsSession = CreateJsCliSessionInfo(env, session);
|
||||
if (jsSession == nullptr) {
|
||||
asyncTask->Reject(env, CreateJsUndefined(env));
|
||||
return;
|
||||
}
|
||||
asyncTask->ResolveWithNoError(env, jsSession);
|
||||
});
|
||||
};
|
||||
int32_t errCode = CliToolMGRClient::GetInstance().ExecTool(param, replyCallback);
|
||||
int32_t errCode = DispatchCliTool(param, env, asyncTask);
|
||||
if (errCode != ERR_OK) {
|
||||
asyncTask->Reject(env, CreateCliJsErrorByNativeErr(env, errCode));
|
||||
}
|
||||
@@ -456,7 +460,7 @@ napi_value JSCliManagerInit(napi_env env, napi_value exportObj)
|
||||
BindNativeFunction(env, exportObj, "subscribeSession", moduleName, JSCliManager::SubscribeSession);
|
||||
BindNativeFunction(env, exportObj, "clearSession", moduleName, JSCliManager::ClearSession);
|
||||
BindNativeFunction(env, exportObj, "querySession", moduleName, JSCliManager::QuerySession);
|
||||
BindNativeFunction(env, exportObj, "sendMsg", moduleName, JSCliManager::SendMessage);
|
||||
BindNativeFunction(env, exportObj, "sendMessage", moduleName, JSCliManager::SendMessage);
|
||||
BindNativeFunction(env, exportObj, "getToolInfoByName", moduleName, JSCliManager::GetToolInfoByName);
|
||||
BindNativeFunction(env, exportObj, "queryToolSummaries", moduleName, JSCliManager::QueryToolSummaries);
|
||||
BindNativeFunction(env, exportObj, "queryTools", moduleName, JSCliManager::QueryTools);
|
||||
|
||||
+20
-83
@@ -21,6 +21,7 @@
|
||||
#include "cli_tool_event.h"
|
||||
#include "exec_options.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "icli_tool_data.h"
|
||||
#include "napi_common_util.h"
|
||||
|
||||
using namespace OHOS::AbilityRuntime;
|
||||
@@ -28,29 +29,6 @@ using namespace OHOS::AbilityRuntime;
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
const std::string ARG_MAPPING_TYPE_FLAG = "flag";
|
||||
const std::string ARG_MAPPING_TYPE_POSITIONAL = "positional";
|
||||
const std::string ARG_MAPPING_TYPE_FLATTENED = "flattened";
|
||||
const std::string ARG_MAPPING_TYPE_JSONSTRING = "jsonString";
|
||||
const std::string ARG_MAPPING_TYPE_MIXED = "mixed";
|
||||
|
||||
std::string ArgMappingTypeToString(ArgMappingType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ArgMappingType::FLAG:
|
||||
return ARG_MAPPING_TYPE_FLAG;
|
||||
case ArgMappingType::POSITIONAL:
|
||||
return ARG_MAPPING_TYPE_POSITIONAL;
|
||||
case ArgMappingType::FLATTENED:
|
||||
return ARG_MAPPING_TYPE_FLATTENED;
|
||||
case ArgMappingType::JSONSTRING:
|
||||
return ARG_MAPPING_TYPE_JSONSTRING;
|
||||
case ArgMappingType::MIXED:
|
||||
return ARG_MAPPING_TYPE_MIXED;
|
||||
default:
|
||||
return ARG_MAPPING_TYPE_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
napi_value ParseJsonStringToJsObject(napi_env env, const std::string &jsonStr)
|
||||
{
|
||||
@@ -207,7 +185,7 @@ bool UnwrapExecOptions(napi_env env, napi_value obj, ExecOptions &options)
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "invalid yieldMs property");
|
||||
return false;
|
||||
}
|
||||
if (!AppExecFwk::UnwrapInt32FromJS2(env, yieldMsProp, options.yieldMs)) {
|
||||
if (!AppExecFwk::UnwrapInt64FromJS2(env, yieldMsProp, options.yieldMs)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "unwrap yieldMs failed");
|
||||
return false;
|
||||
}
|
||||
@@ -223,7 +201,7 @@ bool UnwrapExecOptions(napi_env env, napi_value obj, ExecOptions &options)
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "invalid timeout property");
|
||||
return false;
|
||||
}
|
||||
if (!AppExecFwk::UnwrapInt32FromJS2(env, timeoutProp, options.timeout)) {
|
||||
if (!AppExecFwk::UnwrapInt64FromJS2(env, timeoutProp, options.timeout)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "unwrap timeout failed");
|
||||
return false;
|
||||
}
|
||||
@@ -246,22 +224,29 @@ napi_value CreateJsCliSessionInfo(napi_env env, const CliSessionInfo &session)
|
||||
napi_set_named_property(env, jsObj, "status", AppExecFwk::WrapStringToJS(env, session.status));
|
||||
|
||||
// Set result if present
|
||||
if (session.result != nullptr) {
|
||||
if (session.status != "running" && session.result != nullptr) {
|
||||
napi_value jsResult = nullptr;
|
||||
status = napi_create_object(env, &jsResult);
|
||||
if (status != napi_ok) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to create JS ExecResult");
|
||||
return nullptr;
|
||||
}
|
||||
napi_set_named_property(env, jsResult, "exitCode", AppExecFwk::WrapInt32ToJS(env, session.result->exitCode));
|
||||
// Set outputText
|
||||
napi_value jsOutputText = AppExecFwk::WrapStringToJS(env, session.result->outputText);
|
||||
napi_set_named_property(env, jsResult, "outputText", jsOutputText);
|
||||
// Set errorText
|
||||
napi_set_named_property(env, jsResult, "errorText", AppExecFwk::WrapStringToJS(env, session.result->errorText));
|
||||
// Set signalNumber
|
||||
napi_value jsSignalNumber = AppExecFwk::WrapInt32ToJS(env, session.result->signalNumber);
|
||||
napi_set_named_property(env, jsResult, "signalNumber", jsSignalNumber);
|
||||
if (!session.result->timedOut) {
|
||||
napi_value jsExitCode = AppExecFwk::WrapInt32ToJS(env, session.result->exitCode);
|
||||
napi_set_named_property(env, jsResult, "exitCode", jsExitCode);
|
||||
}
|
||||
if (!session.result->outputText.empty()) {
|
||||
napi_value jsOutputText = AppExecFwk::WrapStringToJS(env, session.result->outputText);
|
||||
napi_set_named_property(env, jsResult, "outputText", jsOutputText);
|
||||
}
|
||||
if (!session.result->errorText.empty()) {
|
||||
napi_value jsErrorText = AppExecFwk::WrapStringToJS(env, session.result->errorText);
|
||||
napi_set_named_property(env, jsResult, "errorText", jsErrorText);
|
||||
}
|
||||
if (session.result->signalNumber != 0) {
|
||||
napi_value jsSignalNumber = AppExecFwk::WrapInt32ToJS(env, session.result->signalNumber);
|
||||
napi_set_named_property(env, jsResult, "signalNumber", jsSignalNumber);
|
||||
}
|
||||
// Set timedOut
|
||||
napi_set_named_property(env, jsResult, "timedOut", AppExecFwk::WrapBoolToJS(env, session.result->timedOut));
|
||||
// Set executionTime
|
||||
@@ -273,34 +258,6 @@ napi_value CreateJsCliSessionInfo(napi_env env, const CliSessionInfo &session)
|
||||
return handleEscape.Escape(jsObj);
|
||||
}
|
||||
|
||||
napi_value CreateJsArgMapping(napi_env env, const ArgMapping &argMapping)
|
||||
{
|
||||
napi_value jsObj = nullptr;
|
||||
napi_status status = napi_create_object(env, &jsObj);
|
||||
if (status != napi_ok) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to create JS object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set type (string: 'flag', 'positional', 'flattened', 'jsonString', 'mixed')
|
||||
napi_value jsType = AppExecFwk::WrapStringToJS(env, ArgMappingTypeToString(argMapping.type));
|
||||
napi_set_named_property(env, jsObj, "type", jsType);
|
||||
|
||||
// Set separator
|
||||
napi_value jsSeparator = AppExecFwk::WrapStringToJS(env, argMapping.separator);
|
||||
napi_set_named_property(env, jsObj, "separator", jsSeparator);
|
||||
|
||||
// Set order
|
||||
napi_value jsOrder = AppExecFwk::WrapStringToJS(env, argMapping.order);
|
||||
napi_set_named_property(env, jsObj, "order", jsOrder);
|
||||
|
||||
// Set templates (parse JSON string to object)
|
||||
napi_value jsTemplates = ParseJsonStringToJsObject(env, argMapping.templates);
|
||||
napi_set_named_property(env, jsObj, "templates", jsTemplates);
|
||||
|
||||
return jsObj;
|
||||
}
|
||||
|
||||
napi_value CreateJsSubCommandInfo(napi_env env, const SubCommandInfo &subcmd)
|
||||
{
|
||||
napi_value jsObj = nullptr;
|
||||
@@ -331,14 +288,6 @@ napi_value CreateJsSubCommandInfo(napi_env env, const SubCommandInfo &subcmd)
|
||||
napi_value jsOutputSchema = ParseJsonStringToJsObject(env, subcmd.outputSchema);
|
||||
napi_set_named_property(env, jsObj, "outputSchema", jsOutputSchema);
|
||||
|
||||
// Set argMapping
|
||||
if (subcmd.argMapping != nullptr) {
|
||||
napi_value jsArgMapping = CreateJsArgMapping(env, *subcmd.argMapping);
|
||||
if (jsArgMapping != nullptr) {
|
||||
napi_set_named_property(env, jsObj, "argMapping", jsArgMapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Set eventTypes (array)
|
||||
napi_value jsEventTypes = nullptr;
|
||||
napi_create_array(env, &jsEventTypes);
|
||||
@@ -440,14 +389,6 @@ napi_value CreateJsToolInfo(napi_env env, const ToolInfo &tool)
|
||||
napi_value jsOutputSchema = ParseJsonStringToJsObject(env, tool.outputSchema);
|
||||
napi_set_named_property(env, jsObj, "outputSchema", jsOutputSchema);
|
||||
|
||||
// Set argMapping
|
||||
if (tool.argMapping != nullptr) {
|
||||
napi_value jsArgMapping = CreateJsArgMapping(env, *tool.argMapping);
|
||||
if (jsArgMapping != nullptr) {
|
||||
napi_set_named_property(env, jsObj, "argMapping", jsArgMapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Set eventTypes (array)
|
||||
napi_value jsEventTypes = nullptr;
|
||||
napi_create_array(env, &jsEventTypes);
|
||||
@@ -461,10 +402,6 @@ napi_value CreateJsToolInfo(napi_env env, const ToolInfo &tool)
|
||||
napi_value jsEventSchemas = ParseJsonStringToJsObject(env, tool.eventSchemas);
|
||||
napi_set_named_property(env, jsObj, "eventSchemas", jsEventSchemas);
|
||||
|
||||
// Set timeout
|
||||
napi_value jsTimeout = AppExecFwk::WrapInt32ToJS(env, tool.timeout);
|
||||
napi_set_named_property(env, jsObj, "timeout", jsTimeout);
|
||||
|
||||
// Set hasSubCommand
|
||||
napi_value jsHasSubCommand = AppExecFwk::WrapBoolToJS(env, tool.hasSubCommand);
|
||||
napi_set_named_property(env, jsObj, "hasSubCommand", jsHasSubCommand);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/ohos.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
ohos_shared_library("scriptmanager_napi") {
|
||||
sanitize = {
|
||||
cfi = true
|
||||
cfi_cross_dso = true
|
||||
debug = false
|
||||
}
|
||||
|
||||
include_dirs = [
|
||||
"include",
|
||||
"${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/js_script_manager.cpp",
|
||||
"src/script_manager_module.cpp",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"${ability_runtime_innerkits_path}/ability_manager:ability_manager",
|
||||
"${ability_runtime_innerkits_path}/napi_base_context:napi_base_context",
|
||||
"${ability_runtime_innerkits_path}/runtime:runtime",
|
||||
"${ability_runtime_napi_path}/inner/napi_common:napi_common",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"ability_base:base",
|
||||
"ability_base:want",
|
||||
"c_utils:utils",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"napi:ace_napi",
|
||||
]
|
||||
|
||||
relative_install_dir = "module/app/ability"
|
||||
subsystem_name = "ability"
|
||||
part_name = "ability_runtime"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_JS_SCRIPT_MANAGER_H
|
||||
#define OHOS_ABILITY_RUNTIME_JS_SCRIPT_MANAGER_H
|
||||
|
||||
#include "native_engine/native_engine.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace AbilityRuntime {
|
||||
|
||||
class JSScriptManager final {
|
||||
public:
|
||||
JSScriptManager() {}
|
||||
~JSScriptManager() {}
|
||||
|
||||
static void Finalizer(napi_env env, void *data, void *hint);
|
||||
static napi_value CompleteArkTSScriptInApp(napi_env env, napi_callback_info info);
|
||||
static napi_value CompleteArkTSScript(napi_env env, napi_callback_info info);
|
||||
|
||||
private:
|
||||
napi_value OnCompleteArkTSScriptInApp(napi_env env, size_t argc, napi_value *argv);
|
||||
napi_value OnCompleteArkTSScript(napi_env env, size_t argc, napi_value *argv);
|
||||
};
|
||||
|
||||
napi_value JSScriptManagerInit(napi_env env, napi_value exportObj);
|
||||
|
||||
} // namespace AbilityRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_JS_SCRIPT_MANAGER_H
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "js_script_manager.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ability_manager_client.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "js_error_utils.h"
|
||||
#include "napi_common_skill_execute.h"
|
||||
#include "napi_common_util.h"
|
||||
#include "napi_base_context.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace AbilityRuntime {
|
||||
namespace {
|
||||
constexpr int32_t INDEX_ZERO = 0;
|
||||
constexpr int32_t INDEX_ONE = 1;
|
||||
constexpr int32_t INDEX_TWO = 2;
|
||||
constexpr int32_t INDEX_THREE = 3;
|
||||
constexpr int32_t ERR_CONTEXT_NOT_ABILITY = 16000020;
|
||||
|
||||
bool HasPropertyOfType(napi_env env, napi_value obj, const char *prop)
|
||||
{
|
||||
napi_value value = nullptr;
|
||||
napi_get_named_property(env, obj, prop, &value);
|
||||
napi_valuetype type = napi_undefined;
|
||||
napi_typeof(env, value, &type);
|
||||
return type == napi_object;
|
||||
}
|
||||
|
||||
bool VerifyContext(napi_env env, napi_value value)
|
||||
{
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
napi_valuetype valueType = napi_undefined;
|
||||
napi_typeof(env, value, &valueType);
|
||||
if (valueType != napi_object) {
|
||||
return false;
|
||||
}
|
||||
return HasPropertyOfType(env, value, "abilityInfo") ||
|
||||
HasPropertyOfType(env, value, "extensionAbilityInfo");
|
||||
}
|
||||
|
||||
void ThrowContextNotValidError(napi_env env)
|
||||
{
|
||||
ThrowError(env, ERR_CONTEXT_NOT_ABILITY,
|
||||
"The context is not a valid ability or extension context.");
|
||||
}
|
||||
|
||||
std::string ParseRequestCode(napi_env env, napi_value value)
|
||||
{
|
||||
napi_valuetype type = napi_undefined;
|
||||
napi_typeof(env, value, &type);
|
||||
if (type == napi_string) {
|
||||
size_t len = 0;
|
||||
napi_get_value_string_utf8(env, value, nullptr, 0, &len);
|
||||
std::string result(len, '\0');
|
||||
napi_get_value_string_utf8(env, value, result.data(), len + 1, &len);
|
||||
return result;
|
||||
}
|
||||
if (type == napi_number) {
|
||||
double val = 0;
|
||||
napi_get_value_double(env, value, &val);
|
||||
return std::to_string(static_cast<int64_t>(val));
|
||||
}
|
||||
if (type == napi_bigint) {
|
||||
bool lossless = true;
|
||||
int64_t requestCode = 0;
|
||||
napi_get_value_bigint_int64(env, value, &requestCode, &lossless);
|
||||
return std::to_string(requestCode);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void JSScriptManager::Finalizer(napi_env env, void *data, void *hint)
|
||||
{
|
||||
std::unique_ptr<JSScriptManager>(static_cast<JSScriptManager *>(data));
|
||||
}
|
||||
|
||||
napi_value JSScriptManager::CompleteArkTSScriptInApp(napi_env env, napi_callback_info info)
|
||||
{
|
||||
GET_CB_INFO_AND_CALL(env, info, JSScriptManager, OnCompleteArkTSScriptInApp);
|
||||
}
|
||||
|
||||
napi_value JSScriptManager::CompleteArkTSScript(napi_env env, napi_callback_info info)
|
||||
{
|
||||
GET_CB_INFO_AND_CALL(env, info, JSScriptManager, OnCompleteArkTSScript);
|
||||
}
|
||||
|
||||
napi_value JSScriptManager::OnCompleteArkTSScriptInApp(napi_env env, size_t argc, napi_value *argv)
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::JSNAPI, "JSScriptManager::OnCompleteArkTSScriptInApp called");
|
||||
HandleEscape handleEscape(env);
|
||||
if (argc < INDEX_THREE) {
|
||||
ThrowTooFewParametersError(env);
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
if (!VerifyContext(env, argv[INDEX_ZERO])) {
|
||||
ThrowContextNotValidError(env);
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
auto context = GetStageModeContext(env, argv[INDEX_ZERO]);
|
||||
sptr<IRemoteObject> token = (context != nullptr) ? context->GetToken() : nullptr;
|
||||
if (token == nullptr) {
|
||||
ThrowInvalidParamError(env, "failed to get token from context");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
std::string requestCode = ParseRequestCode(env, argv[INDEX_ONE]);
|
||||
if (requestCode.empty()) {
|
||||
ThrowInvalidParamError(env, "requestCode must be a non-empty string");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
AppExecFwk::SkillExecuteResult skillResult;
|
||||
if (!UnwrapSkillExecuteResult(env, argv[INDEX_TWO], skillResult)) {
|
||||
ThrowInvalidParamError(env, "result must be a valid ExecuteResult");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
TAG_LOGD(AAFwkTag::JSNAPI,
|
||||
"completeArkTSScriptInApp reqCode:%{public}s code:%{public}d",
|
||||
requestCode.c_str(), skillResult.code);
|
||||
auto innerErrCode = std::make_shared<int32_t>(ERR_OK);
|
||||
NapiAsyncTask::ExecuteCallback execute =
|
||||
[innerErrCode, token, requestCode, skillResult]() {
|
||||
*innerErrCode = AAFwk::AbilityManagerClient::GetInstance()->ExecuteSkillDone(
|
||||
token, requestCode, skillResult.code, skillResult);
|
||||
};
|
||||
NapiAsyncTask::CompleteCallback complete =
|
||||
[innerErrCode](napi_env env, NapiAsyncTask &task, int32_t status) {
|
||||
HandleScope handleScope(env);
|
||||
if (*innerErrCode != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::JSNAPI,
|
||||
"completeArkTSScriptInApp error: %{public}d", *innerErrCode);
|
||||
task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
|
||||
return;
|
||||
}
|
||||
task.ResolveWithNoError(env, CreateJsUndefined(env));
|
||||
};
|
||||
napi_value asyncResult = nullptr;
|
||||
NapiAsyncTask::Schedule("JSScriptManager::OnCompleteArkTSScriptInApp", env,
|
||||
CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute),
|
||||
std::move(complete), &asyncResult));
|
||||
return handleEscape.Escape(asyncResult);
|
||||
}
|
||||
|
||||
napi_value JSScriptManager::OnCompleteArkTSScript(napi_env env, size_t argc, napi_value *argv)
|
||||
{
|
||||
TAG_LOGW(AAFwkTag::JSNAPI,
|
||||
"completeArkTSScript is not supported for independent skill yet");
|
||||
ThrowError(env, 401, "completeArkTSScript is not supported yet");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
|
||||
napi_value JSScriptManagerInit(napi_env env, napi_value exportObj)
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::JSNAPI, "Init JSScriptManager");
|
||||
|
||||
if (env == nullptr || exportObj == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::JSNAPI, "Null env or exportObj");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<JSScriptManager> jsScriptManager = std::make_unique<JSScriptManager>();
|
||||
napi_wrap(env, exportObj, jsScriptManager.release(),
|
||||
JSScriptManager::Finalizer, nullptr, nullptr);
|
||||
|
||||
const char *moduleName = "ScriptManager";
|
||||
BindNativeFunction(env, exportObj, "completeArkTSScriptInApp", moduleName,
|
||||
JSScriptManager::CompleteArkTSScriptInApp);
|
||||
BindNativeFunction(env, exportObj, "completeArkTSScript", moduleName,
|
||||
JSScriptManager::CompleteArkTSScript);
|
||||
|
||||
TAG_LOGD(AAFwkTag::JSNAPI, "JSScriptManagerInit end");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
|
||||
} // namespace AbilityRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "native_engine/native_engine.h"
|
||||
#include "js_script_manager.h"
|
||||
|
||||
static napi_module _module = {
|
||||
.nm_version = 0,
|
||||
.nm_filename = "app/ability/scriptmanager_napi.so/script_manager.js",
|
||||
.nm_register_func = OHOS::AbilityRuntime::JSScriptManagerInit,
|
||||
.nm_modname = "app.ability.scriptManager",
|
||||
};
|
||||
|
||||
extern "C" __attribute__((constructor)) void NAPI_application_ScriptManager_AutoRegister(void)
|
||||
{
|
||||
napi_module_register(&_module);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/ohos.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
ohos_shared_library("skilldriver_napi") {
|
||||
sanitize = {
|
||||
cfi = true
|
||||
cfi_cross_dso = true
|
||||
debug = false
|
||||
}
|
||||
|
||||
include_dirs = [
|
||||
"include",
|
||||
"${cli_tool_framework_path}/frameworks/js/napi/cli_tool_manager/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/js_skill_driver.cpp",
|
||||
"src/skill_driver_module.cpp",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"${ability_runtime_innerkits_path}/ability_manager:ability_manager",
|
||||
"${ability_runtime_innerkits_path}/runtime:runtime",
|
||||
"${ability_runtime_napi_path}/inner/napi_common:napi_common",
|
||||
"//foundation/ability/ability_runtime/cli_tool_framework/frameworks/js/napi/cli_tool_manager:cli_manager_error_utils_src",
|
||||
"//foundation/ability/ability_runtime/cli_tool_framework/frameworks/js/napi/cli_tool_manager:js_cli_event_handler_manager_src",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"ability_base:base",
|
||||
"ability_base:want",
|
||||
"c_utils:utils",
|
||||
"eventhandler:libeventhandler",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"napi:ace_napi",
|
||||
]
|
||||
|
||||
relative_install_dir = "module/app/ability"
|
||||
subsystem_name = "ability"
|
||||
part_name = "ability_runtime"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_JS_SKILL_DRIVER_H
|
||||
#define OHOS_ABILITY_RUNTIME_JS_SKILL_DRIVER_H
|
||||
|
||||
#include "native_engine/native_engine.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
class JSSkillDriver final {
|
||||
public:
|
||||
JSSkillDriver() {}
|
||||
~JSSkillDriver() {}
|
||||
|
||||
static void Finalizer(napi_env env, void *data, void *hint);
|
||||
static napi_value ExecSkillTool(napi_env env, napi_callback_info info);
|
||||
|
||||
private:
|
||||
napi_value OnExecSkillTool(napi_env env, size_t argc, napi_value *argv);
|
||||
};
|
||||
|
||||
napi_value JSSkillDriverInit(napi_env env, napi_value exportObj);
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_JS_SKILL_DRIVER_H
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "js_skill_driver.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "ability_manager_client.h"
|
||||
#include "array_wrapper.h"
|
||||
#include "cli_error_code.h"
|
||||
#include "bool_wrapper.h"
|
||||
#include "cli_manager_error_utils.h"
|
||||
#include "double_wrapper.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "int_wrapper.h"
|
||||
#include "js_cli_event_handler_manager.h"
|
||||
#include "js_error_utils.h"
|
||||
#include "js_runtime_utils.h"
|
||||
#include "long_wrapper.h"
|
||||
#include "napi_common_skill_execute.h"
|
||||
#include "napi_common_util.h"
|
||||
#include "napi_common_want.h"
|
||||
#include "skill/skill_execute_callback_stub.h"
|
||||
#include "string_wrapper.h"
|
||||
#include "want_params.h"
|
||||
|
||||
using namespace OHOS::AbilityRuntime;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t INDEX_ZERO = 0;
|
||||
constexpr int32_t INDEX_TWO = 2;
|
||||
|
||||
std::string GetStringPropertyFromJs(napi_env env, napi_value obj, const std::string &key)
|
||||
{
|
||||
napi_value value = nullptr;
|
||||
napi_get_named_property(env, obj, key.c_str(), &value);
|
||||
if (value == nullptr) {
|
||||
return "";
|
||||
}
|
||||
std::string result;
|
||||
if (!AppExecFwk::UnwrapStringFromJS2(env, value, result)) {
|
||||
return "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetPropertyKeyFromJs(napi_env env, napi_value keyVal)
|
||||
{
|
||||
size_t strLen = 0;
|
||||
napi_get_value_string_utf8(env, keyVal, nullptr, 0, &strLen);
|
||||
std::string key(strLen, '\0');
|
||||
napi_get_value_string_utf8(env, keyVal, key.data(), strLen + 1, &strLen);
|
||||
return key;
|
||||
}
|
||||
|
||||
void SetSkillArrayString(const std::string &key, const std::vector<std::string> &values,
|
||||
AAFwk::WantParams ¶ms)
|
||||
{
|
||||
auto arr = sptr<AAFwk::IArray>(new (std::nothrow) AAFwk::Array(values.size(), AAFwk::g_IID_IString));
|
||||
if (arr == nullptr) { return; }
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
arr->Set(i, AAFwk::String::Box(values[i]));
|
||||
}
|
||||
params.SetParam(key, arr);
|
||||
}
|
||||
|
||||
void SetSkillArrayBool(const std::string &key, const std::vector<bool> &values,
|
||||
AAFwk::WantParams ¶ms)
|
||||
{
|
||||
auto arr = sptr<AAFwk::IArray>(new (std::nothrow) AAFwk::Array(values.size(), AAFwk::g_IID_IBoolean));
|
||||
if (arr == nullptr) { return; }
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
arr->Set(i, AAFwk::Boolean::Box(values[i]));
|
||||
}
|
||||
params.SetParam(key, arr);
|
||||
}
|
||||
|
||||
void SetSkillArrayDouble(const std::string &key, const std::vector<double> &values,
|
||||
AAFwk::WantParams ¶ms)
|
||||
{
|
||||
auto arr = sptr<AAFwk::IArray>(new (std::nothrow) AAFwk::Array(values.size(), AAFwk::g_IID_IDouble));
|
||||
if (arr == nullptr) { return; }
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
arr->Set(i, AAFwk::Double::Box(values[i]));
|
||||
}
|
||||
params.SetParam(key, arr);
|
||||
}
|
||||
|
||||
void SetSkillArrayLong(const std::string &key, const std::vector<int64_t> &values,
|
||||
AAFwk::WantParams ¶ms)
|
||||
{
|
||||
auto arr = sptr<AAFwk::IArray>(new (std::nothrow) AAFwk::Array(values.size(), AAFwk::g_IID_ILong));
|
||||
if (arr == nullptr) { return; }
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
arr->Set(i, AAFwk::Long::Box(values[i]));
|
||||
}
|
||||
params.SetParam(key, arr);
|
||||
}
|
||||
|
||||
void SetSkillArrayParam(napi_env env, const std::string &key, napi_value val,
|
||||
AAFwk::WantParams ¶ms)
|
||||
{
|
||||
uint32_t size = 0;
|
||||
if (!AppExecFwk::IsArrayForNapiValue(env, val, size) || size == 0) {
|
||||
return;
|
||||
}
|
||||
napi_value elem = nullptr;
|
||||
napi_get_element(env, val, 0, &elem);
|
||||
if (elem == nullptr) { return; }
|
||||
|
||||
napi_valuetype elemType = napi_undefined;
|
||||
napi_typeof(env, elem, &elemType);
|
||||
switch (elemType) {
|
||||
case napi_string: {
|
||||
std::vector<std::string> values;
|
||||
if (AppExecFwk::UnwrapArrayStringFromJS(env, val, values)) {
|
||||
SetSkillArrayString(key, values, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case napi_number: {
|
||||
std::vector<double> dblValues;
|
||||
if (AppExecFwk::UnwrapArrayDoubleFromJS(env, val, dblValues)) {
|
||||
SetSkillArrayDouble(key, dblValues, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case napi_boolean: {
|
||||
std::vector<bool> values;
|
||||
if (AppExecFwk::UnwrapArrayBoolFromJS(env, val, values)) {
|
||||
SetSkillArrayBool(key, values, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case napi_bigint: {
|
||||
std::vector<int64_t> values;
|
||||
if (AppExecFwk::UnwrapArrayInt64FromJS(env, val, values)) {
|
||||
SetSkillArrayLong(key, values, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetSkillParamByType(napi_env env, const std::string &key, napi_value val, AAFwk::WantParams ¶ms)
|
||||
{
|
||||
napi_valuetype type = napi_undefined;
|
||||
napi_typeof(env, val, &type);
|
||||
switch (type) {
|
||||
case napi_string: {
|
||||
std::string str;
|
||||
if (AppExecFwk::UnwrapStringFromJS2(env, val, str)) {
|
||||
params.SetParam(key, AAFwk::String::Box(str));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case napi_number: {
|
||||
double dblVal = 0.0;
|
||||
napi_get_value_double(env, val, &dblVal);
|
||||
int32_t intVal = static_cast<int32_t>(dblVal);
|
||||
if (static_cast<double>(intVal) == dblVal) {
|
||||
params.SetParam(key, AAFwk::Integer::Box(intVal));
|
||||
} else {
|
||||
params.SetParam(key, AAFwk::Double::Box(dblVal));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case napi_boolean: {
|
||||
bool boolVal = false;
|
||||
napi_get_value_bool(env, val, &boolVal);
|
||||
params.SetParam(key, AAFwk::Boolean::Box(boolVal));
|
||||
break;
|
||||
}
|
||||
case napi_bigint: {
|
||||
int64_t int64Val = 0;
|
||||
bool lossless = true;
|
||||
napi_get_value_bigint_int64(env, val, &int64Val, &lossless);
|
||||
params.SetParam(key, AAFwk::Long::Box(int64Val));
|
||||
break;
|
||||
}
|
||||
case napi_object: {
|
||||
SetSkillArrayParam(env, key, val, params);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<AAFwk::WantParams> ExtractSkillArgs(napi_env env, napi_value obj)
|
||||
{
|
||||
auto skillArgs = std::make_shared<AAFwk::WantParams>();
|
||||
napi_value propertyNames = nullptr;
|
||||
napi_get_property_names(env, obj, &propertyNames);
|
||||
if (propertyNames == nullptr) {
|
||||
return skillArgs;
|
||||
}
|
||||
const std::set<std::string> reservedKeys = {
|
||||
"skillToolType", "bundleName", "moduleName", "skillName", "arkTSPath", "funcName"
|
||||
};
|
||||
uint32_t length = 0;
|
||||
napi_get_array_length(env, propertyNames, &length);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
napi_value keyVal = nullptr;
|
||||
napi_get_element(env, propertyNames, i, &keyVal);
|
||||
if (keyVal == nullptr) { continue; }
|
||||
std::string key = GetPropertyKeyFromJs(env, keyVal);
|
||||
if (key.empty() || reservedKeys.count(key) > 0) { continue; }
|
||||
napi_value val = nullptr;
|
||||
napi_get_named_property(env, obj, key.c_str(), &val);
|
||||
if (val == nullptr) { continue; }
|
||||
SetSkillParamByType(env, key, val, *skillArgs);
|
||||
}
|
||||
return skillArgs;
|
||||
}
|
||||
|
||||
class SkillExecuteCallbackImpl : public AAFwk::SkillExecuteCallbackStub {
|
||||
public:
|
||||
explicit SkillExecuteCallbackImpl(napi_env env, napi_deferred deferred)
|
||||
: env_(env), deferred_(deferred) {}
|
||||
|
||||
void OnExecuteDone(const std::string &requestCode, int32_t resultCode,
|
||||
const AppExecFwk::SkillExecuteResult &result) override
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL,
|
||||
"SkillExecuteCallbackImpl::OnExecuteDone req:%{public}s code:%{public}d",
|
||||
requestCode.c_str(), resultCode);
|
||||
auto resultCopy = result;
|
||||
auto deferred = deferred_;
|
||||
JsCliEventHandlerManager::GetInstance().PostTask(
|
||||
[env = env_, deferred, resultCopy]() {
|
||||
HandleScope handleScope(env);
|
||||
napi_value jsResult = WrapSkillExecuteResult(env, resultCopy);
|
||||
napi_resolve_deferred(env, deferred, jsResult);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
napi_env env_ = nullptr;
|
||||
napi_deferred deferred_ = nullptr;
|
||||
};
|
||||
|
||||
int32_t DispatchExecuteSkill(const std::string &bundleName, const std::string &moduleName,
|
||||
const std::string &skillName, const std::string &arkTSPath, const std::string &funcName,
|
||||
const std::shared_ptr<AAFwk::WantParams> &skillArgs,
|
||||
const sptr<SkillExecuteCallbackImpl> &callback)
|
||||
{
|
||||
constexpr int32_t SKILL_TYPE_INDEPENDENT = -1;
|
||||
int32_t skillType = 0;
|
||||
auto queryRet = AAFwk::AbilityManagerClient::GetInstance()->QuerySkillType(
|
||||
bundleName, moduleName, skillName, skillType);
|
||||
if (queryRet != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "querySkillType failed:%{public}d", queryRet);
|
||||
return queryRet;
|
||||
}
|
||||
if (skillType == SKILL_TYPE_INDEPENDENT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "independent skill not supported yet");
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
return AAFwk::AbilityManagerClient::GetInstance()->ExecuteInAppSkill(
|
||||
bundleName, moduleName, skillName, arkTSPath, funcName, skillArgs, callback);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void JSSkillDriver::Finalizer(napi_env env, void *data, void *hint)
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "JSSkillDriver::Finalizer is called");
|
||||
std::unique_ptr<JSSkillDriver>(static_cast<JSSkillDriver *>(data));
|
||||
}
|
||||
|
||||
napi_value JSSkillDriver::ExecSkillTool(napi_env env, napi_callback_info info)
|
||||
{
|
||||
GET_CB_INFO_AND_CALL(env, info, JSSkillDriver, OnExecSkillTool);
|
||||
}
|
||||
|
||||
napi_value JSSkillDriver::OnExecSkillTool(napi_env env, size_t argc, napi_value *argv)
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "JSSkillDriver::OnExecSkillTool called");
|
||||
HandleEscape handleEscape(env);
|
||||
if (argc < INDEX_TWO) {
|
||||
ThrowTooFewParametersError(env);
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
napi_valuetype valueType = napi_undefined;
|
||||
napi_typeof(env, argv[INDEX_ZERO], &valueType);
|
||||
if (valueType != napi_object) {
|
||||
ThrowInvalidParamError(env, "skillToolParam must be an object");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
auto skillToolType = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "skillToolType");
|
||||
if (skillToolType.empty()) {
|
||||
ThrowInvalidParamError(env, "skillToolType is required");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
auto bundleName = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "bundleName");
|
||||
auto moduleName = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "moduleName");
|
||||
auto skillName = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "skillName");
|
||||
if (bundleName.empty() || moduleName.empty() || skillName.empty()) {
|
||||
ThrowInvalidParamError(env, "bundleName, moduleName, skillName are required");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
auto arkTSPath = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "arkTSPath");
|
||||
auto funcName = GetStringPropertyFromJs(env, argv[INDEX_ZERO], "funcName");
|
||||
auto skillArgs = ExtractSkillArgs(env, argv[INDEX_ZERO]);
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL,
|
||||
"execSkillTool bundle:%{public}s module:%{public}s skill:%{public}s "
|
||||
"type:%{public}s",
|
||||
bundleName.c_str(), moduleName.c_str(), skillName.c_str(),
|
||||
skillToolType.c_str());
|
||||
|
||||
napi_deferred deferred = nullptr;
|
||||
napi_value promise = nullptr;
|
||||
napi_create_promise(env, &deferred, &promise);
|
||||
|
||||
auto innerErrCode = std::make_shared<int32_t>(ERR_OK);
|
||||
auto callback = sptr<SkillExecuteCallbackImpl>::MakeSptr(env, deferred);
|
||||
|
||||
NapiAsyncTask::ExecuteCallback execute =
|
||||
[innerErrCode, bundleName, moduleName, skillName,
|
||||
arkTSPath, funcName, skillArgs, callback]() {
|
||||
*innerErrCode = DispatchExecuteSkill(
|
||||
bundleName, moduleName, skillName, arkTSPath, funcName, skillArgs, callback);
|
||||
};
|
||||
|
||||
NapiAsyncTask::CompleteCallback complete =
|
||||
[innerErrCode, deferred](napi_env env, NapiAsyncTask &task, int32_t status) {
|
||||
HandleScope handleScope(env);
|
||||
if (*innerErrCode != ERR_OK) {
|
||||
napi_reject_deferred(env, deferred,
|
||||
CreateCliJsErrorByNativeErr(env, *innerErrCode));
|
||||
}
|
||||
};
|
||||
|
||||
auto asyncTask = std::make_unique<NapiAsyncTask>(deferred,
|
||||
std::make_unique<NapiAsyncTask::ExecuteCallback>(std::move(execute)),
|
||||
std::make_unique<NapiAsyncTask::CompleteCallback>(std::move(complete)));
|
||||
NapiAsyncTask::Schedule("JSSkillDriver::OnExecSkillTool", env, std::move(asyncTask));
|
||||
return handleEscape.Escape(promise);
|
||||
}
|
||||
|
||||
napi_value JSSkillDriverInit(napi_env env, napi_value exportObj)
|
||||
{
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "Init JSSkillDriver");
|
||||
|
||||
if (env == nullptr || exportObj == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "Null env or exportObj");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<JSSkillDriver> jsSkillDriver = std::make_unique<JSSkillDriver>();
|
||||
napi_wrap(env, exportObj, jsSkillDriver.release(), JSSkillDriver::Finalizer, nullptr, nullptr);
|
||||
|
||||
const char *moduleName = "SkillDriver";
|
||||
BindNativeFunction(env, exportObj, "execSkillTool", moduleName, JSSkillDriver::ExecSkillTool);
|
||||
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "JSSkillDriverInit end");
|
||||
return CreateJsUndefined(env);
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "native_engine/native_engine.h"
|
||||
#include "js_skill_driver.h"
|
||||
|
||||
static napi_module _module = {
|
||||
.nm_version = 0,
|
||||
.nm_filename = "app/ability/skilldriver_napi.so/skill_driver.js",
|
||||
.nm_register_func = OHOS::CliTool::JSSkillDriverInit,
|
||||
.nm_modname = "app.ability.skillDriver",
|
||||
};
|
||||
|
||||
extern "C" __attribute__((constructor)) void NAPI_application_SkillDriver_AutoRegister(void)
|
||||
{
|
||||
napi_module_register(&_module);
|
||||
}
|
||||
@@ -19,10 +19,9 @@ idl_gen_interface("cli_tool_manager_interface") {
|
||||
sources = [
|
||||
"ICliToolManager.idl",
|
||||
"ICliToolManagerScheduler.idl",
|
||||
"IExecToolCallback.idl",
|
||||
]
|
||||
|
||||
sources_common = [ "ICliToolCmd.idl" ]
|
||||
sources_common = [ "ICliToolData.idl" ]
|
||||
hitrace = "HITRACE_TAG_ABILITY_MANAGER"
|
||||
log_domainid = "0xD001365"
|
||||
log_tag = "CliToolManager"
|
||||
@@ -51,7 +50,6 @@ ohos_shared_library("cli_tool_client") {
|
||||
public_configs = [ ":cli_tool_client_config" ]
|
||||
|
||||
sources = [
|
||||
"src/arg_mapping.cpp",
|
||||
"src/cli_mgr_load_callback.cpp",
|
||||
"src/cli_event_reply_manager.cpp",
|
||||
"src/cli_session_info.cpp",
|
||||
@@ -61,7 +59,6 @@ ohos_shared_library("cli_tool_client") {
|
||||
"src/cli_tool_mgr_scheduler_recipient.cpp",
|
||||
"src/exec_options.cpp",
|
||||
"src/exec_result.cpp",
|
||||
"src/exec_tool_callback_impl.cpp",
|
||||
"src/exec_tool_param.cpp",
|
||||
"src/sub_command_info.cpp",
|
||||
"src/tool_info.cpp",
|
||||
|
||||
@@ -15,19 +15,18 @@
|
||||
|
||||
package OHOS.CliTool;
|
||||
|
||||
import ICliToolCmd;
|
||||
|
||||
import ICliToolData;
|
||||
import ICliToolManagerScheduler;
|
||||
sequenceable CliSessionInfo..OHOS.CliTool.CliSessionInfo;
|
||||
sequenceable ExecToolParam..OHOS.CliTool.ExecToolParam;
|
||||
sequenceable OHOS.CliTool.ToolSummary;
|
||||
sequenceable OHOS.IRemoteObject;
|
||||
sequenceable ToolInfo..OHOS.CliTool.ToolInfo;
|
||||
rawdata ToolInfo..OHOS.CliTool.ToolsRawData;
|
||||
|
||||
interface ICliToolManager {
|
||||
void GetAllToolSummaries([out] ToolSummary[] summaries);
|
||||
void GetToolInfoByName([in] String name, [out] ToolInfo tool);
|
||||
void GetAllToolInfos([out] ToolInfo[] tools);
|
||||
void GetAllToolInfos([out] ToolsRawData tools);
|
||||
void RegisterTool([in] ToolInfo tool);
|
||||
void ExecTool([in] ExecToolParam param, [in] String eventId);
|
||||
void SubscribeSession([in] String sessionId, [in] String subscriptionId);
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
package OHOS.CliTool;
|
||||
|
||||
sequenceable CliToolEvent..OHOS.CliTool.CliToolEvent;
|
||||
sequenceable CliSessionInfo..OHOS.CliTool.CliSessionInfo;
|
||||
sequenceable CliToolEvent..OHOS.CliTool.CliToolEvent;
|
||||
|
||||
interface ICliToolManagerScheduler {
|
||||
[oneway]void SchedulerSessionEvent([in] String sessionId, [in] String subscriptionId, [in] CliToolEvent event);
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_ARG_MAPPING_H
|
||||
#define OHOS_ABILITY_RUNTIME_ARG_MAPPING_H
|
||||
|
||||
#include <memory>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <parcel.h>
|
||||
#include <string>
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
/**
|
||||
* @brief Enum for argument mapping type
|
||||
*/
|
||||
enum class ArgMappingType {
|
||||
FLAG = 0,
|
||||
POSITIONAL = 1,
|
||||
FLATTENED = 2,
|
||||
JSONSTRING = 3,
|
||||
MIXED = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Argument mapping structure
|
||||
*/
|
||||
class ArgMapping : public Parcelable {
|
||||
public:
|
||||
ArgMappingType type = ArgMappingType::FLAG;
|
||||
std::string separator;
|
||||
std::string order;
|
||||
std::string templates; // JSON string
|
||||
|
||||
ArgMapping() = default;
|
||||
~ArgMapping() = default;
|
||||
|
||||
bool Marshalling(Parcel &parcel) const override;
|
||||
static ArgMapping *Unmarshalling(Parcel &parcel);
|
||||
|
||||
/**
|
||||
* @brief Parse ArgMapping from JSON object
|
||||
* @param json Input JSON object
|
||||
* @param argMapping Output ArgMapping
|
||||
* @return bool true if parse success and required fields are valid
|
||||
*/
|
||||
static bool ParseFromJson(const nlohmann::json &json, ArgMapping &argMapping);
|
||||
|
||||
/**
|
||||
* @brief Convert ArgMapping to JSON object
|
||||
*/
|
||||
nlohmann::json ParseToJson() const;
|
||||
|
||||
/**
|
||||
* @brief Validate ArgMapping fields
|
||||
* @param argMapping ArgMapping to validate
|
||||
* @return bool true if valid
|
||||
*/
|
||||
static bool Validate(const ArgMapping &argMapping);
|
||||
};
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_ARG_MAPPING_H
|
||||
@@ -75,6 +75,11 @@ enum {
|
||||
* Result (35700011): The caller is not SA.
|
||||
*/
|
||||
ERR_NOT_SA_CALLER = 35700011,
|
||||
|
||||
/*
|
||||
* Result (35700012): fail to kill.
|
||||
*/
|
||||
ERR_NOT_KILL = 35700012,
|
||||
};
|
||||
|
||||
} // namespace CliTool
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
std::string sessionId;
|
||||
std::string toolName;
|
||||
std::string status; // "running", "completed", "failed"
|
||||
std::shared_ptr<ExecResult> result = nullptr; // optional, only when status="completed"
|
||||
std::shared_ptr<ExecResult> result = nullptr; // optional, only when status="completed" and status="failed"
|
||||
|
||||
CliSessionInfo() = default;
|
||||
|
||||
@@ -41,4 +41,4 @@ public:
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_SESSION_INFO_H
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_SESSION_INFO_H
|
||||
@@ -30,8 +30,8 @@ namespace CliTool {
|
||||
class ExecOptions : public Parcelable {
|
||||
public:
|
||||
bool background = false;
|
||||
int32_t yieldMs = 0;
|
||||
int32_t timeout = 0;
|
||||
int64_t yieldMs = 0;
|
||||
int64_t timeout = 0;
|
||||
|
||||
bool Marshalling(Parcel &parcel) const;
|
||||
static ExecOptions *Unmarshalling(Parcel &parcel);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace CliTool {
|
||||
*/
|
||||
class ExecResult : public Parcelable {
|
||||
public:
|
||||
int32_t exitCode = -1;
|
||||
int32_t exitCode = 1;
|
||||
std::string outputText = "";
|
||||
std::string errorText = "";
|
||||
int32_t signalNumber = 0;
|
||||
@@ -39,4 +39,4 @@ public:
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_ABILITY_RUNTIME_EXEC_RESULT_H
|
||||
#endif // OHOS_ABILITY_RUNTIME_EXEC_RESULT_H
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_EXEC_TOOL_CALLBACK_IMPL_H
|
||||
#define OHOS_ABILITY_RUNTIME_EXEC_TOOL_CALLBACK_IMPL_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "cli_session_info.h"
|
||||
#include "exec_tool_callback_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
using ExecToolResultTask = std::function<void(const CliSessionInfo &session)>;
|
||||
}
|
||||
|
||||
class ExecToolCallbackImpl : public ExecToolCallbackStub {
|
||||
public:
|
||||
explicit ExecToolCallbackImpl(ExecToolResultTask &&task) : task_(task) {}
|
||||
virtual ~ExecToolCallbackImpl() = default;
|
||||
|
||||
int32_t SendResult(const CliSessionInfo &session) override;
|
||||
|
||||
private:
|
||||
ExecToolResultTask task_;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_ABILITY_RUNTIME_EXEC_TOOL_CALLBACK_IMPL_H
|
||||
@@ -16,8 +16,6 @@
|
||||
#ifndef OHOS_ABILITY_RUNTIME_SUB_COMMAND_INFO_H
|
||||
#define OHOS_ABILITY_RUNTIME_SUB_COMMAND_INFO_H
|
||||
|
||||
#include "arg_mapping.h"
|
||||
|
||||
#include <memory>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <parcel.h>
|
||||
@@ -36,7 +34,6 @@ public:
|
||||
std::vector<std::string> requirePermissions;
|
||||
std::string inputSchema; // JSON string
|
||||
std::string outputSchema; // JSON string
|
||||
std::shared_ptr<ArgMapping> argMapping;
|
||||
std::vector<std::string> eventTypes;
|
||||
std::string eventSchemas; // JSON string
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#ifndef OHOS_ABILITY_RUNTIME_TOOL_INFO_H
|
||||
#define OHOS_ABILITY_RUNTIME_TOOL_INFO_H
|
||||
|
||||
#include "arg_mapping.h"
|
||||
#include "sub_command_info.h"
|
||||
#include "tool_summary.h"
|
||||
|
||||
@@ -29,38 +28,25 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "exec_result.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
class ToolInfo;
|
||||
|
||||
/**
|
||||
* @brief Raw data type for IDL serialization
|
||||
* @brief Raw data type for IDL serialization (shared memory optimization)
|
||||
*/
|
||||
class ToolsRawData : public Parcelable {
|
||||
class ToolsRawData {
|
||||
public:
|
||||
std::vector<uint32_t> data;
|
||||
std::string ownedData;
|
||||
uint32_t size = 0;
|
||||
const void* data = nullptr;
|
||||
bool isMalloc = false;
|
||||
|
||||
ToolsRawData() = default;
|
||||
~ToolsRawData() = default;
|
||||
|
||||
bool Marshalling(Parcel &parcel) const override
|
||||
{
|
||||
if (!parcel.WriteUInt32Vector(data)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ToolsRawData *Unmarshalling(Parcel &parcel)
|
||||
{
|
||||
ToolsRawData *rawdata = new (std::nothrow) ToolsRawData();
|
||||
if (rawdata && !parcel.ReadUInt32Vector(&rawdata->data)) {
|
||||
delete rawdata;
|
||||
return nullptr;
|
||||
}
|
||||
return rawdata;
|
||||
}
|
||||
static void FromToolInfoVec(const std::vector<ToolInfo> &tools, ToolsRawData &rawData);
|
||||
static int32_t ToToolInfoVec(const ToolsRawData &rawData, std::vector<ToolInfo> &tools);
|
||||
int32_t RawDataCpy(const void *readdata);
|
||||
~ToolsRawData();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,10 +61,8 @@ public:
|
||||
std::vector<std::string> requirePermissions;
|
||||
std::string inputSchema; // JSON string
|
||||
std::string outputSchema; // JSON string
|
||||
std::shared_ptr<ArgMapping> argMapping;
|
||||
std::vector<std::string> eventTypes;
|
||||
std::string eventSchemas; // JSON string (map of event type to schema)
|
||||
int32_t timeout = 1800;
|
||||
bool hasSubCommand = false;
|
||||
std::map<std::string, SubCommandInfo> subcommands;
|
||||
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "arg_mapping.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
bool ArgMapping::Marshalling(Parcel &parcel) const
|
||||
{
|
||||
if (!parcel.WriteInt32(static_cast<int32_t>(type))) {
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteString(separator)) {
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteString(order)) {
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteString(templates)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ArgMapping *ArgMapping::Unmarshalling(Parcel &parcel)
|
||||
{
|
||||
auto mapping = std::make_unique<ArgMapping>();
|
||||
|
||||
int32_t typeValue = 0;
|
||||
if (!parcel.ReadInt32(typeValue)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadString(mapping->separator)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadString(mapping->order)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadString(mapping->templates)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mapping->type = static_cast<ArgMappingType>(typeValue);
|
||||
return mapping.release();
|
||||
}
|
||||
|
||||
bool ArgMapping::ParseFromJson(const nlohmann::json &json, ArgMapping &argMapping)
|
||||
{
|
||||
// type is required
|
||||
if (!json.contains("type") || !json["type"].is_string()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string typeStr = json["type"];
|
||||
if (typeStr == "flag") {
|
||||
argMapping.type = ArgMappingType::FLAG;
|
||||
} else if (typeStr == "positional") {
|
||||
argMapping.type = ArgMappingType::POSITIONAL;
|
||||
} else if (typeStr == "flattened") {
|
||||
argMapping.type = ArgMappingType::FLATTENED;
|
||||
} else if (typeStr == "jsonString") {
|
||||
argMapping.type = ArgMappingType::JSONSTRING;
|
||||
} else if (typeStr == "mixed") {
|
||||
argMapping.type = ArgMappingType::MIXED;
|
||||
} else {
|
||||
return false; // invalid type value
|
||||
}
|
||||
|
||||
if (json.contains("separator")) {
|
||||
if (!json["separator"].is_string()) {
|
||||
return false; // separator must be a string
|
||||
}
|
||||
argMapping.separator = json["separator"];
|
||||
}
|
||||
if (json.contains("order")) {
|
||||
if (!json["order"].is_string()) {
|
||||
return false; // order must be a string
|
||||
}
|
||||
argMapping.order = json["order"];
|
||||
}
|
||||
if (json.contains("templates")) {
|
||||
if (!json["templates"].is_object()) {
|
||||
return false; // templates must be an object
|
||||
}
|
||||
argMapping.templates = json["templates"].dump();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nlohmann::json ArgMapping::ParseToJson() const
|
||||
{
|
||||
nlohmann::json j;
|
||||
switch (type) {
|
||||
case ArgMappingType::FLAG:
|
||||
j["type"] = "flag";
|
||||
break;
|
||||
case ArgMappingType::POSITIONAL:
|
||||
j["type"] = "positional";
|
||||
break;
|
||||
case ArgMappingType::FLATTENED:
|
||||
j["type"] = "flattened";
|
||||
break;
|
||||
case ArgMappingType::JSONSTRING:
|
||||
j["type"] = "jsonString";
|
||||
break;
|
||||
case ArgMappingType::MIXED:
|
||||
j["type"] = "mixed";
|
||||
break;
|
||||
}
|
||||
if (!separator.empty()) {
|
||||
j["separator"] = separator;
|
||||
}
|
||||
if (!order.empty()) {
|
||||
j["order"] = order;
|
||||
}
|
||||
if (!templates.empty()) {
|
||||
nlohmann::json templatesJson = nlohmann::json::parse(templates, nullptr, false);
|
||||
if (!templatesJson.is_discarded()) {
|
||||
j["templates"] = templatesJson;
|
||||
} else {
|
||||
j["templates"] = templates;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
bool ArgMapping::Validate(const ArgMapping &argMapping)
|
||||
{
|
||||
// type must be valid enum value
|
||||
int32_t typeValue = static_cast<int32_t>(argMapping.type);
|
||||
if (typeValue < 0 || typeValue > static_cast<int32_t>(ArgMappingType::MIXED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// templates must be valid JSON object if not empty
|
||||
if (!argMapping.templates.empty()) {
|
||||
nlohmann::json templatesJson = nlohmann::json::parse(argMapping.templates, nullptr, false);
|
||||
if (templatesJson.is_discarded() || !templatesJson.is_object()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -31,7 +31,6 @@ bool CliSessionInfo::Marshalling(Parcel &parcel) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write result presence flag
|
||||
bool hasResult = (result != nullptr);
|
||||
if (!parcel.WriteBool(hasResult)) {
|
||||
return false;
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
#include "cli_tool_mgr_client.h"
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "cli_event_reply_manager.h"
|
||||
#include "cli_mgr_load_callback.h"
|
||||
#include "cli_session_subscription_manager.h"
|
||||
#include "cli_tool_mgr_scheduler_recipient.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "hitrace_meter.h"
|
||||
#include "if_system_ability_manager.h"
|
||||
#include "iservice_registry.h"
|
||||
#include "system_ability_definition.h"
|
||||
#include "cli_mgr_load_callback.h"
|
||||
|
||||
#include "cli_event_reply_manager.h"
|
||||
#include "cli_session_subscription_manager.h"
|
||||
#include "cli_tool_mgr_scheduler_recipient.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
@@ -130,7 +129,13 @@ ErrCode CliToolMGRClient::GetAllToolInfos(std::vector<ToolInfo> &tools)
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "proxy is null");
|
||||
return GET_CLI_TOOL_MGR_SERVICE_FAILED;
|
||||
}
|
||||
return proxy->GetAllToolInfos(tools);
|
||||
ToolsRawData rawData;
|
||||
auto ret = proxy->GetAllToolInfos(rawData);
|
||||
if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "GetAllToolInfos failed: %{public}d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ToolsRawData::ToToolInfoVec(rawData, tools);
|
||||
}
|
||||
|
||||
ErrCode CliToolMGRClient::RegisterTool(const ToolInfo &tool)
|
||||
@@ -196,7 +201,8 @@ sptr<ICliToolManager> CliToolMGRClient::GetCliToolMgrProxy()
|
||||
|
||||
const auto &onClearProxyCallback = [](const wptr<IRemoteObject> &remote) {
|
||||
auto &instance = GetInstance();
|
||||
if (instance.cliToolMgr_ == remote) {
|
||||
auto cliToolMgr = instance.GetCliToolMgr();
|
||||
if (cliToolMgr != nullptr && cliToolMgr->AsObject() == remote) {
|
||||
instance.ClearProxy();
|
||||
}
|
||||
};
|
||||
@@ -281,7 +287,7 @@ void CliToolMGRClient::ClearProxy()
|
||||
void CliToolMGRClient::CliMgrDeathRecipient::OnRemoteDied(const wptr<IRemoteObject> &remote)
|
||||
{
|
||||
if (callback_ != nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "cli tool manager service died");
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "cli tool manager service died");
|
||||
callback_(remote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ bool ExecOptions::Marshalling(Parcel &parcel) const
|
||||
if (!parcel.WriteBool(background)) {
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteInt32(yieldMs)) {
|
||||
if (!parcel.WriteInt64(yieldMs)) {
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteInt32(timeout)) {
|
||||
if (!parcel.WriteInt64(timeout)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -34,15 +34,18 @@ bool ExecOptions::Marshalling(Parcel &parcel) const
|
||||
ExecOptions *ExecOptions::Unmarshalling(Parcel &parcel)
|
||||
{
|
||||
auto *options = new (std::nothrow) ExecOptions();
|
||||
if (options && !parcel.ReadBool(options->background)) {
|
||||
if (options == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadBool(options->background)) {
|
||||
delete options;
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadInt32(options->yieldMs)) {
|
||||
if (!parcel.ReadInt64(options->yieldMs)) {
|
||||
delete options;
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadInt32(options->timeout)) {
|
||||
if (!parcel.ReadInt64(options->timeout)) {
|
||||
delete options;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -38,14 +38,6 @@ bool SubCommandInfo::Marshalling(Parcel &parcel) const
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to write outputSchema");
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteBool(argMapping != nullptr)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to write hasArgMapping flag");
|
||||
return false;
|
||||
}
|
||||
if (argMapping != nullptr && !argMapping->Marshalling(parcel)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to write argMapping");
|
||||
return false;
|
||||
}
|
||||
if (!parcel.WriteStringVector(eventTypes)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to write eventTypes");
|
||||
return false;
|
||||
@@ -65,7 +57,6 @@ SubCommandInfo *SubCommandInfo::Unmarshalling(Parcel &parcel)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hasArgMapping = false;
|
||||
if (!parcel.ReadString(subCmd->description)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to read description");
|
||||
delete subCmd;
|
||||
@@ -86,21 +77,6 @@ SubCommandInfo *SubCommandInfo::Unmarshalling(Parcel &parcel)
|
||||
delete subCmd;
|
||||
return nullptr;
|
||||
}
|
||||
if (!parcel.ReadBool(hasArgMapping)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to read hasArgMapping flag");
|
||||
delete subCmd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (hasArgMapping) {
|
||||
subCmd->argMapping.reset(ArgMapping::Unmarshalling(parcel));
|
||||
if (subCmd->argMapping == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to unmarshal argMapping");
|
||||
delete subCmd;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parcel.ReadStringVector(&subCmd->eventTypes)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to read eventTypes");
|
||||
delete subCmd;
|
||||
@@ -119,59 +95,55 @@ bool SubCommandInfo::ParseFromJson(const nlohmann::json &json, SubCommandInfo &s
|
||||
{
|
||||
// description is required and must be non-empty
|
||||
if (!json.contains("description") || !json["description"].is_string()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: description is missing or not a string");
|
||||
return false;
|
||||
}
|
||||
std::string description = json["description"];
|
||||
if (description.empty()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: description is empty");
|
||||
return false;
|
||||
}
|
||||
subCmd.description = description;
|
||||
|
||||
// requirePermissions is optional, but if present must be array of strings
|
||||
if (json.contains("requirePermissions")) {
|
||||
if (!json["requirePermissions"].is_array()) {
|
||||
// requirePermissions is required and must be array
|
||||
if (!json.contains("requirePermissions") || !json["requirePermissions"].is_array()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: requirePermissions is missing or not an array");
|
||||
return false;
|
||||
}
|
||||
for (const auto &perm : json["requirePermissions"]) {
|
||||
if (!perm.is_string()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: requirePermissions contains non-string item");
|
||||
return false;
|
||||
}
|
||||
for (const auto &perm : json["requirePermissions"]) {
|
||||
if (!perm.is_string()) {
|
||||
return false;
|
||||
}
|
||||
std::string permStr = perm;
|
||||
if (!permStr.empty()) {
|
||||
subCmd.requirePermissions.push_back(std::move(permStr));
|
||||
}
|
||||
std::string permStr = perm;
|
||||
if (!permStr.empty()) {
|
||||
subCmd.requirePermissions.push_back(std::move(permStr));
|
||||
}
|
||||
}
|
||||
|
||||
// inputSchema is required and must be JSON object
|
||||
if (!json.contains("inputSchema") || !json["inputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: inputSchema is missing or not a JSON object");
|
||||
return false;
|
||||
}
|
||||
subCmd.inputSchema = json["inputSchema"].dump();
|
||||
|
||||
// outputSchema is required and must be JSON object
|
||||
if (!json.contains("outputSchema") || !json["outputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: outputSchema is missing or not a JSON object");
|
||||
return false;
|
||||
}
|
||||
subCmd.outputSchema = json["outputSchema"].dump();
|
||||
|
||||
// argMapping is required
|
||||
if (!json.contains("argMapping") || !json["argMapping"].is_object()) {
|
||||
return false;
|
||||
}
|
||||
subCmd.argMapping = std::make_shared<ArgMapping>();
|
||||
if (!ArgMapping::ParseFromJson(json["argMapping"], *subCmd.argMapping)) {
|
||||
subCmd.argMapping = nullptr;
|
||||
return false; // argMapping parse failed
|
||||
}
|
||||
|
||||
// eventTypes is optional, but if present must be array of strings
|
||||
if (json.contains("eventTypes")) {
|
||||
if (!json["eventTypes"].is_array()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: eventTypes is not an array");
|
||||
return false;
|
||||
}
|
||||
for (const auto &evt : json["eventTypes"]) {
|
||||
if (!evt.is_string()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: eventTypes contains non-string item");
|
||||
return false;
|
||||
}
|
||||
std::string evtStr = evt;
|
||||
@@ -184,6 +156,7 @@ bool SubCommandInfo::ParseFromJson(const nlohmann::json &json, SubCommandInfo &s
|
||||
// eventSchemas is optional, but if present must be JSON object
|
||||
if (json.contains("eventSchemas")) {
|
||||
if (!json["eventSchemas"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: eventSchemas is not a JSON object");
|
||||
return false;
|
||||
}
|
||||
subCmd.eventSchemas = json["eventSchemas"].dump();
|
||||
@@ -223,9 +196,6 @@ nlohmann::json SubCommandInfo::ParseToJson() const
|
||||
json["eventSchemas"] = eventSchemas;
|
||||
}
|
||||
}
|
||||
if (argMapping != nullptr) {
|
||||
json["argMapping"] = argMapping->ParseToJson();
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
@@ -260,16 +230,6 @@ bool SubCommandInfo::Validate(const SubCommandInfo &subCmd)
|
||||
return false;
|
||||
}
|
||||
|
||||
// argMapping is required
|
||||
if (subCmd.argMapping == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: argMapping is null");
|
||||
return false;
|
||||
}
|
||||
if (!ArgMapping::Validate(*subCmd.argMapping)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: argMapping validation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// eventSchemas: if not empty, must be valid JSON object
|
||||
if (!subCmd.eventSchemas.empty()) {
|
||||
nlohmann::json eventSchemasJson = nlohmann::json::parse(subCmd.eventSchemas, nullptr, false);
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "securec.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t MAX_TOOL_INFO_COUNT = 200000;
|
||||
} // namespace
|
||||
|
||||
// ToolInfo implementation
|
||||
bool ToolInfo::Marshalling(Parcel &parcel) const
|
||||
{
|
||||
@@ -43,10 +49,7 @@ bool ToolInfo::Marshalling(Parcel &parcel) const
|
||||
parcel.WriteStringVector(requirePermissions) &&
|
||||
parcel.WriteString(inputSchema) &&
|
||||
parcel.WriteString(outputSchema) &&
|
||||
parcel.WriteBool(argMapping != nullptr) &&
|
||||
(argMapping == nullptr || argMapping->Marshalling(parcel)) &&
|
||||
parcel.WriteString(eventSchemas) &&
|
||||
parcel.WriteInt32(timeout) &&
|
||||
parcel.WriteStringVector(eventTypes) &&
|
||||
parcel.WriteBool(hasSubCommand) &&
|
||||
parcel.WriteString(subcommandsJson);
|
||||
@@ -59,7 +62,6 @@ ToolInfo *ToolInfo::Unmarshalling(Parcel &parcel)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hasArgMapping = false;
|
||||
std::string subcommandsJson;
|
||||
if (!parcel.ReadString(tool->name) ||
|
||||
!parcel.ReadString(tool->version) ||
|
||||
@@ -68,21 +70,7 @@ ToolInfo *ToolInfo::Unmarshalling(Parcel &parcel)
|
||||
!parcel.ReadStringVector(&tool->requirePermissions) ||
|
||||
!parcel.ReadString(tool->inputSchema) ||
|
||||
!parcel.ReadString(tool->outputSchema) ||
|
||||
!parcel.ReadBool(hasArgMapping)) {
|
||||
delete tool;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (hasArgMapping) {
|
||||
tool->argMapping.reset(ArgMapping::Unmarshalling(parcel));
|
||||
if (tool->argMapping == nullptr) {
|
||||
delete tool;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parcel.ReadString(tool->eventSchemas) ||
|
||||
!parcel.ReadInt32(tool->timeout) ||
|
||||
!parcel.ReadString(tool->eventSchemas) ||
|
||||
!parcel.ReadStringVector(&tool->eventTypes) ||
|
||||
!parcel.ReadBool(tool->hasSubCommand) ||
|
||||
!parcel.ReadString(subcommandsJson)) {
|
||||
@@ -117,7 +105,7 @@ bool ToolInfo::ValidateName(const std::string &name)
|
||||
// Must start with "ohos-" or "hms-"
|
||||
const std::string OHOS_PREFIX = "ohos-";
|
||||
const std::string HMS_PREFIX = "hms-";
|
||||
const size_t MAX_SUFFIX_LENGTH = 16;
|
||||
const size_t MAX_SUFFIX_LENGTH = 32;
|
||||
|
||||
bool hasValidPrefix = false;
|
||||
size_t suffixStart = 0;
|
||||
@@ -134,7 +122,7 @@ bool ToolInfo::ValidateName(const std::string &name)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suffix must not exceed 16 characters
|
||||
// Suffix must not exceed 32 characters
|
||||
std::string suffix = name.substr(suffixStart);
|
||||
if (suffix.empty() || suffix.size() > MAX_SUFFIX_LENGTH) {
|
||||
return false;
|
||||
@@ -247,44 +235,38 @@ bool ToolInfo::ParseFromJson(const nlohmann::json &json, ToolInfo &tool)
|
||||
}
|
||||
tool.executablePath = executablePath;
|
||||
|
||||
if (json.contains("requirePermissions") && json["requirePermissions"].is_array()) {
|
||||
std::vector<std::string> perms;
|
||||
for (const auto &perm : json["requirePermissions"]) {
|
||||
if (!perm.is_string()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: requirePermissions contains non-string item");
|
||||
return false;
|
||||
}
|
||||
std::string permStr = perm.get<std::string>();
|
||||
if (!permStr.empty()) {
|
||||
perms.push_back(std::move(permStr));
|
||||
}
|
||||
}
|
||||
tool.requirePermissions = std::move(perms);
|
||||
}
|
||||
if (json.contains("inputSchema")) {
|
||||
if (!json["inputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: inputSchema is not a JSON object");
|
||||
return false;
|
||||
}
|
||||
tool.inputSchema = json["inputSchema"].dump();
|
||||
}
|
||||
if (json.contains("outputSchema")) {
|
||||
if (!json["outputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: outputSchema is not a JSON object");
|
||||
return false;
|
||||
}
|
||||
tool.outputSchema = json["outputSchema"].dump();
|
||||
}
|
||||
if (!json.contains("argMapping") || !json["argMapping"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: argMapping is required and must be an object");
|
||||
// requirePermissions is required and must be array
|
||||
if (!json.contains("requirePermissions") || !json["requirePermissions"].is_array()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: requirePermissions is missing or not an array");
|
||||
return false;
|
||||
}
|
||||
tool.argMapping = std::make_shared<ArgMapping>();
|
||||
if (!ArgMapping::ParseFromJson(json["argMapping"], *tool.argMapping)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: argMapping parse failed");
|
||||
tool.argMapping = nullptr;
|
||||
std::vector<std::string> perms;
|
||||
for (const auto &perm : json["requirePermissions"]) {
|
||||
if (!perm.is_string()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: requirePermissions contains non-string item");
|
||||
return false;
|
||||
}
|
||||
std::string permStr = perm.get<std::string>();
|
||||
if (!permStr.empty()) {
|
||||
perms.push_back(std::move(permStr));
|
||||
}
|
||||
}
|
||||
tool.requirePermissions = std::move(perms);
|
||||
|
||||
// inputSchema is required and must be a JSON object
|
||||
if (!json.contains("inputSchema") || !json["inputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: inputSchema is missing or not a JSON object");
|
||||
return false;
|
||||
}
|
||||
tool.inputSchema = json["inputSchema"].dump();
|
||||
|
||||
// outputSchema is required and must be a JSON object
|
||||
if (!json.contains("outputSchema") || !json["outputSchema"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: outputSchema is missing or not a JSON object");
|
||||
return false;
|
||||
}
|
||||
tool.outputSchema = json["outputSchema"].dump();
|
||||
|
||||
if (json.contains("eventSchemas")) {
|
||||
if (!json["eventSchemas"].is_object()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: eventSchemas is not a JSON object");
|
||||
@@ -292,19 +274,6 @@ bool ToolInfo::ParseFromJson(const nlohmann::json &json, ToolInfo &tool)
|
||||
}
|
||||
tool.eventSchemas = json["eventSchemas"].dump();
|
||||
}
|
||||
if (json.contains("timeout")) {
|
||||
if (!json["timeout"].is_number_integer()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: timeout is not an integer");
|
||||
return false;
|
||||
}
|
||||
int32_t timeoutValue = json["timeout"];
|
||||
if (timeoutValue <= 0 || timeoutValue > 1800) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: timeout %{public}d is out of range (0, 1800]",
|
||||
timeoutValue);
|
||||
return false;
|
||||
}
|
||||
tool.timeout = timeoutValue;
|
||||
}
|
||||
if (json.contains("eventTypes")) {
|
||||
if (!json["eventTypes"].is_array()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: eventTypes is not an array");
|
||||
@@ -374,9 +343,6 @@ nlohmann::json ToolInfo::ParseToJson() const
|
||||
j["outputSchema"] = outputSchema;
|
||||
}
|
||||
}
|
||||
if (argMapping != nullptr) {
|
||||
j["argMapping"] = argMapping->ParseToJson();
|
||||
}
|
||||
if (!eventSchemas.empty()) {
|
||||
nlohmann::json eventSchemasJson = nlohmann::json::parse(eventSchemas, nullptr, false);
|
||||
if (!eventSchemasJson.is_discarded()) {
|
||||
@@ -385,7 +351,6 @@ nlohmann::json ToolInfo::ParseToJson() const
|
||||
j["eventSchemas"] = eventSchemas;
|
||||
}
|
||||
}
|
||||
j["timeout"] = timeout;
|
||||
j["eventTypes"] = eventTypes;
|
||||
j["hasSubCommand"] = hasSubCommand;
|
||||
if (!subcommands.empty()) {
|
||||
@@ -426,38 +391,25 @@ bool ToolInfo::Validate(const ToolInfo &tool)
|
||||
return false;
|
||||
}
|
||||
|
||||
// inputSchema: if not empty, must be valid JSON string
|
||||
if (!tool.inputSchema.empty()) {
|
||||
nlohmann::json inputSchemaJson = nlohmann::json::parse(tool.inputSchema, nullptr, false);
|
||||
if (inputSchemaJson.is_discarded()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: inputSchema is not valid JSON");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// outputSchema: if not empty, must be valid JSON string
|
||||
if (!tool.outputSchema.empty()) {
|
||||
nlohmann::json outputSchemaJson = nlohmann::json::parse(tool.outputSchema, nullptr, false);
|
||||
if (outputSchemaJson.is_discarded()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: outputSchema is not valid JSON");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// argMapping is required
|
||||
if (tool.argMapping == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: argMapping is null");
|
||||
// inputSchema is required and must be valid JSON string
|
||||
if (tool.inputSchema.empty()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: inputSchema is empty");
|
||||
return false;
|
||||
}
|
||||
if (!ArgMapping::Validate(*tool.argMapping)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: argMapping validation failed");
|
||||
nlohmann::json inputSchemaJson = nlohmann::json::parse(tool.inputSchema, nullptr, false);
|
||||
if (inputSchemaJson.is_discarded()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: inputSchema is not valid JSON");
|
||||
return false;
|
||||
}
|
||||
|
||||
// timeout must be > 0 and <= 1800
|
||||
if (tool.timeout <= 0 || tool.timeout > 1800) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: timeout %{public}d is out of range (0, 1800]",
|
||||
tool.timeout);
|
||||
// outputSchema is required and must be valid JSON string
|
||||
if (tool.outputSchema.empty()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: outputSchema is empty");
|
||||
return false;
|
||||
}
|
||||
nlohmann::json outputSchemaJson = nlohmann::json::parse(tool.outputSchema, nullptr, false);
|
||||
if (outputSchemaJson.is_discarded()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Validate failed: outputSchema is not valid JSON");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -479,5 +431,94 @@ bool ToolInfo::Validate(const ToolInfo &tool)
|
||||
return true;
|
||||
}
|
||||
|
||||
ToolsRawData::~ToolsRawData()
|
||||
{
|
||||
if (data != nullptr && isMalloc) {
|
||||
free(const_cast<void*>(data));
|
||||
isMalloc = false;
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ToolsRawData::RawDataCpy(const void *readdata)
|
||||
{
|
||||
if (readdata == nullptr || size == 0) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "null data or zero size");
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
void* newData = malloc(size);
|
||||
if (newData == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "malloc failed");
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
if (memcpy_s(newData, size, readdata, size) != EOK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "memcpy_s failed");
|
||||
free(newData);
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
if (data != nullptr && isMalloc) {
|
||||
free(const_cast<void*>(data));
|
||||
data = nullptr;
|
||||
}
|
||||
data = newData;
|
||||
isMalloc = true;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void ToolsRawData::FromToolInfoVec(const std::vector<ToolInfo> &tools, ToolsRawData &rawData)
|
||||
{
|
||||
std::stringstream ss;
|
||||
uint32_t count = tools.size();
|
||||
ss.write(reinterpret_cast<const char*>(&count), sizeof(count));
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
std::string dumped = tools[i].ParseToJson().dump();
|
||||
uint32_t strLen = dumped.length();
|
||||
ss.write(reinterpret_cast<const char*>(&strLen), sizeof(strLen));
|
||||
ss.write(dumped.c_str(), strLen);
|
||||
}
|
||||
std::string result = ss.str();
|
||||
rawData.ownedData = std::move(result);
|
||||
rawData.data = rawData.ownedData.data();
|
||||
rawData.size = rawData.ownedData.size();
|
||||
rawData.isMalloc = false;
|
||||
}
|
||||
|
||||
int32_t ToolsRawData::ToToolInfoVec(const ToolsRawData &rawData, std::vector<ToolInfo> &tools)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.write(reinterpret_cast<const char *>(rawData.data), rawData.size);
|
||||
ss.seekg(0, std::ios::beg);
|
||||
uint32_t ssLength = static_cast<uint32_t>(ss.str().length());
|
||||
uint32_t count = 0;
|
||||
ss.read(reinterpret_cast<char *>(&count), sizeof(count));
|
||||
if (count > MAX_TOOL_INFO_COUNT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "tools exceed maxSize %{public}d, count: %{public}d",
|
||||
MAX_TOOL_INFO_COUNT, count);
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
tools.resize(count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
uint32_t toolSize = 0;
|
||||
ss.read(reinterpret_cast<char *>(&toolSize), sizeof(toolSize));
|
||||
if (toolSize > ssLength - static_cast<uint32_t>(ss.tellg())) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "toolSize:%{public}u is invalid", toolSize);
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
std::string toolStr(toolSize, '\0');
|
||||
ss.read(toolStr.data(), toolSize);
|
||||
nlohmann::json jsonObject = nlohmann::json::parse(toolStr, nullptr, false, true);
|
||||
if (jsonObject.is_discarded()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "json parse failed, index: %{public}u", i);
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
if (!ToolInfo::ParseFromJson(jsonObject, tools[i])) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed, index: %{public}u", i);
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -39,8 +39,10 @@ ohos_shared_library("climgr") {
|
||||
configs = [ ":clisa_config" ]
|
||||
include_dirs = [ "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper" ]
|
||||
sources = [
|
||||
"src/cli_tool_app_state_observer.cpp",
|
||||
"src/cli_tool_data_manager.cpp",
|
||||
"src/cli_tool_manager_service.cpp",
|
||||
"src/permission_query_util.cpp",
|
||||
"src/process_manager.cpp",
|
||||
"src/session_record.cpp",
|
||||
"src/tool_util.cpp",
|
||||
@@ -52,6 +54,8 @@ ohos_shared_library("climgr") {
|
||||
defines = [ "AMS_LOG_TAG = \"CliToolManager\"" ]
|
||||
|
||||
deps = [
|
||||
"${ability_runtime_innerkits_path}/ability_manager:ability_manager",
|
||||
"${ability_runtime_innerkits_path}/app_manager:app_manager",
|
||||
"${ability_runtime_native_path}/appkit:appkit_manager_helper",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_APP_STATE_OBSERVER_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_TOOL_APP_STATE_OBSERVER_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "application_state_observer_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
class CliToolAppStateObserver : public AppExecFwk::ApplicationStateObserverStub {
|
||||
public:
|
||||
using ProcessDiedCallback = std::function<void(const std::string&, pid_t)>;
|
||||
|
||||
explicit CliToolAppStateObserver(const std::string &bundleName, ProcessDiedCallback callback);
|
||||
~CliToolAppStateObserver() override = default;
|
||||
|
||||
void OnProcessDied(const AppExecFwk::ProcessData &processData) override;
|
||||
|
||||
private:
|
||||
std::string bundleName_;
|
||||
ProcessDiedCallback processDiedCallback_;
|
||||
};
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_TOOL_APP_STATE_OBSERVER_H
|
||||
@@ -42,6 +42,13 @@ public:
|
||||
*/
|
||||
int32_t GetAllTools(std::vector<ToolInfo> &tools);
|
||||
|
||||
/**
|
||||
* @brief Get all tools as raw data (shared memory optimization)
|
||||
* @param rawData Output ToolsRawData
|
||||
* @return int32_t ERR_OK on success, error code otherwise
|
||||
*/
|
||||
int32_t GetAllToolsRawData(ToolsRawData &rawData);
|
||||
|
||||
/**
|
||||
* @brief Get tool by name from KVStore
|
||||
* @param name Tool name
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_SERVICE_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_SERVICE_H
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "cli_tool_manager_stub.h"
|
||||
@@ -30,15 +33,24 @@
|
||||
#include "io_monitor.h"
|
||||
#include "process_manager.h"
|
||||
#include "session_record.h"
|
||||
#include "skill/skill_execute_callback_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace AppExecFwk {
|
||||
class IApplicationStateObserver;
|
||||
struct SkillExecuteResult;
|
||||
}
|
||||
namespace CliTool {
|
||||
class SessionRecord;
|
||||
class SkillCallbackAdapter;
|
||||
class CliToolManagerService;
|
||||
|
||||
class CliToolManagerService : public SystemAbility,
|
||||
public CliToolManagerStub,
|
||||
public std::enable_shared_from_this<CliToolManagerService> {
|
||||
public CliToolManagerStub {
|
||||
DECLARE_SYSTEM_ABILITY(CliToolManagerService);
|
||||
|
||||
friend class SkillCallbackAdapter;
|
||||
|
||||
public:
|
||||
static sptr<CliToolManagerService> GetInstance();
|
||||
virtual ~CliToolManagerService() = default;
|
||||
@@ -46,7 +58,7 @@ public:
|
||||
/**
|
||||
* @brief Query all available tools
|
||||
*/
|
||||
int32_t GetAllToolInfos(std::vector<ToolInfo> &tools) override;
|
||||
int32_t GetAllToolInfos(ToolsRawData &tools) override;
|
||||
|
||||
/**
|
||||
* @brief Query tool summaries (lightweight for listing)
|
||||
@@ -92,15 +104,34 @@ public:
|
||||
protected:
|
||||
void OnStart() override;
|
||||
void OnStop() override;
|
||||
int32_t OnIdle(const SystemAbilityOnDemandReason &idlReason) override;
|
||||
|
||||
private:
|
||||
CliToolManagerService() : SystemAbility(CLI_TOOL_MGR_SERVICE_ID, false) {};
|
||||
|
||||
// RAII wrapper for interface call counter
|
||||
class InterfaceCallCounter {
|
||||
public:
|
||||
explicit InterfaceCallCounter(std::atomic<int32_t>& counter) : counter_(counter)
|
||||
{
|
||||
counter_.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
~InterfaceCallCounter()
|
||||
{
|
||||
counter_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
}
|
||||
InterfaceCallCounter(const InterfaceCallCounter&) = delete;
|
||||
InterfaceCallCounter& operator=(const InterfaceCallCounter&) = delete;
|
||||
private:
|
||||
std::atomic<int32_t>& counter_;
|
||||
};
|
||||
|
||||
enum class ServiceRunningState { STATE_NOT_START, STATE_RUNNING };
|
||||
|
||||
void Init();
|
||||
void DelayUnloadTask();
|
||||
|
||||
std::shared_ptr<SessionRecord> CreateSessionRecord(const ExecToolParam ¶m);
|
||||
std::shared_ptr<SessionRecord> CreateSessionRecord(const ExecToolParam ¶m, const std::string &eventId);
|
||||
void AddSessionRecord(const std::shared_ptr<SessionRecord> &record);
|
||||
std::shared_ptr<SessionRecord> GetSessionRecord(const std::string &sessionId);
|
||||
void RemoveSessionRecord(const std::string &sessionId);
|
||||
@@ -108,6 +139,23 @@ private:
|
||||
bool RegisterSessionWithMonitors(const std::shared_ptr<SessionRecord> &record, const ExecToolParam ¶m);
|
||||
void UnregisterSessionWithMonitors(const std::string &sessionId);
|
||||
|
||||
int32_t ValidateExecToolPermissions();
|
||||
int32_t ValidateSessionLimit();
|
||||
int32_t ValidateAndPrepareTool(const ExecToolParam ¶m, uint32_t tokenId,
|
||||
ToolInfo &toolInfo, std::string &sandboxConfig, std::string &bundleName);
|
||||
int32_t SetupAndStartSession(const ExecToolParam ¶m, const std::string &eventId,
|
||||
const ToolInfo &toolInfo, const std::string &sandboxConfig, const std::string &bundleName);
|
||||
|
||||
int32_t SetupAndStartSkillSession(const ExecToolParam ¶m,
|
||||
const std::string &eventId, const ToolInfo &toolInfo);
|
||||
int32_t ValidateSkillTypeFromParam(const ExecToolParam ¶m, int32_t &skillType);
|
||||
int32_t ValidateSkillType(const std::string &bundleName,
|
||||
const std::string &moduleName, const std::string &skillName, int32_t &skillType);
|
||||
void HandleSkillSessionComplete(const std::string &sessionId, int32_t callerPid,
|
||||
const std::string &eventId, int32_t resultCode, const CliSessionInfo &session);
|
||||
void HandleSkillSessionTimeout(const std::string &sessionId);
|
||||
|
||||
void HandleBackgroundSessionReply(const std::shared_ptr<SessionRecord> &record, const std::string &eventId);
|
||||
void HandleProcessTimeout(const std::string &sessionId);
|
||||
void HandleProcessYieldTimeout(const std::string &sessionId);
|
||||
void HandleOutputClosed(const std::string &sessionId, bool isStdout);
|
||||
@@ -121,31 +169,31 @@ private:
|
||||
|
||||
static void sigchld_handler(int32_t sig);
|
||||
|
||||
void PostExecToolTask(int32_t time, const std::string &sessionId, bool isTimeout);
|
||||
void PostExecToolTask(int64_t time, const std::string &sessionId, bool isTimeout);
|
||||
void WaitPid(pid_t pid, int32_t status, int32_t sig);
|
||||
void RegisterAppStateObserver(const std::string &bundleName, pid_t callerPid);
|
||||
void OnProcessDied(const std::string &bundleName, pid_t diedPid);
|
||||
|
||||
bool initialized_ = false;
|
||||
std::shared_ptr<IOMonitor> ioMonitor_ = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Execute single command permission query.
|
||||
*/
|
||||
int32_t DoQueryPermission(const Command &cmd, std::vector<std::string> &permissions);
|
||||
|
||||
/**
|
||||
* @brief Query main command permissions.
|
||||
*/
|
||||
int32_t QueryMainCommandPermission(const std::string &toolName, std::vector<std::string> &permissions);
|
||||
|
||||
/**
|
||||
* @brief Query subcommand permissions.
|
||||
*/
|
||||
int32_t QuerySubCommandPermission(const std::string &toolName, const std::string &subCommand,
|
||||
std::vector<std::string> &permissions);
|
||||
|
||||
std::atomic<int32_t> activeSessionCount_ = 0;
|
||||
std::mutex sessionsMutex_;
|
||||
std::atomic<int32_t> interfaceCalledCount_ = 0;
|
||||
ffrt::mutex sessionsMutex_;
|
||||
std::unordered_map<std::string, std::shared_ptr<SessionRecord>> sessionRecords_;
|
||||
std::unordered_map<std::string, sptr<AppExecFwk::IApplicationStateObserver>> bundleObservers_;
|
||||
};
|
||||
|
||||
class SkillCallbackAdapter : public AAFwk::SkillExecuteCallbackStub {
|
||||
public:
|
||||
SkillCallbackAdapter(const std::string &sessionId,
|
||||
int32_t callerPid, const std::string &eventId);
|
||||
void OnExecuteDone(const std::string &requestCode, int32_t resultCode,
|
||||
const AppExecFwk::SkillExecuteResult &result) override;
|
||||
|
||||
private:
|
||||
std::string sessionId_;
|
||||
int32_t callerPid_;
|
||||
std::string eventId_;
|
||||
};
|
||||
|
||||
} // namespace CliTool
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_EVENT_DISPATCHER_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_EVENT_DISPATCHER_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
#define OHOS_ABILITY_RUNTIME_IO_MONITOR_H
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -57,8 +59,22 @@ private:
|
||||
bool isStdin = false;
|
||||
};
|
||||
|
||||
struct PendingInput {
|
||||
std::string message;
|
||||
std::string eventId;
|
||||
};
|
||||
|
||||
struct InputQueue {
|
||||
std::deque<PendingInput> pendingInputs;
|
||||
size_t pendingBytes = 0;
|
||||
bool writeTaskRunning = false;
|
||||
};
|
||||
|
||||
int GetStdinFd(const std::string &sessionId);
|
||||
void WriteTask(const std::string &sessionId, const std::string &message, const std::string &eventId);
|
||||
int GetStdinFdLocked(const std::string &sessionId) const;
|
||||
bool WriteMessage(int fd, const std::string &sessionId, const std::string &message);
|
||||
void ProcessWriteQueue(const std::string &sessionId);
|
||||
void NotifyInputReply(const std::string &sessionId, const std::string &eventId, bool result);
|
||||
|
||||
void MonitorLoop();
|
||||
void HandleReadableFd(int fd);
|
||||
@@ -70,6 +86,7 @@ private:
|
||||
int epollFd_ = -1;
|
||||
std::mutex fdMutex_;
|
||||
std::unordered_map<int, FdInfo> fdMap_;
|
||||
std::unordered_map<std::string, InputQueue> inputQueues_;
|
||||
OutputCallback outputCallback_;
|
||||
InputReplyCallback inputReplyCallback_;
|
||||
SessionClosedCallback sessionClosedCallback_;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H
|
||||
#define OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "cli_error_code.h"
|
||||
#include "icli_tool_data.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
/**
|
||||
* @brief Query result codes for batch permission query
|
||||
*/
|
||||
namespace QueryResult {
|
||||
constexpr int32_t SUCCESS = 0;
|
||||
constexpr int32_t COMMAND_NOT_EXIST = 1;
|
||||
constexpr int32_t DB_ERROR = 2;
|
||||
} // namespace QueryResult
|
||||
|
||||
/**
|
||||
* @brief CLI tool permission query utility class
|
||||
* Provides static methods to handle command permission query logic
|
||||
*/
|
||||
class PermissionQueryUtil {
|
||||
public:
|
||||
/**
|
||||
* @brief Batch query command permissions
|
||||
* @param cmds Command list to query
|
||||
* @param cmdPermissions Output vector of CommandPermission query results
|
||||
* @return ERR_OK on success
|
||||
*/
|
||||
static int32_t BatchQueryPermissions(
|
||||
const std::vector<Command> &cmds,
|
||||
std::vector<CommandPermission> &cmdPermissions);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Query permissions for a single command
|
||||
* @param cmd Command to query
|
||||
* @param permissions Output vector of permission strings
|
||||
* @return ERR_OK on success
|
||||
* ERR_TOOL_NOT_EXIST when tool not found
|
||||
* ERR_NO_INIT on database error
|
||||
*/
|
||||
static int32_t QuerySingleCommand(
|
||||
const Command &cmd,
|
||||
std::vector<std::string> &permissions);
|
||||
|
||||
/**
|
||||
* @brief Query permissions for main command (no subcommand)
|
||||
* @param toolName Tool name
|
||||
* @param permissions Output vector of permission strings
|
||||
* @return ERR_OK on success
|
||||
* ERR_TOOL_NOT_EXIST when tool not found
|
||||
* ERR_NO_INIT on database error
|
||||
*/
|
||||
static int32_t QueryMainCommandPermission(
|
||||
const std::string &toolName,
|
||||
std::vector<std::string> &permissions);
|
||||
|
||||
/**
|
||||
* @brief Query permissions for subcommand
|
||||
* @param toolName Tool name
|
||||
* @param subCommand Subcommand name
|
||||
* @param permissions Output vector of permission strings
|
||||
* @return ERR_OK on success
|
||||
* ERR_TOOL_NOT_EXIST when tool or subcommand not found
|
||||
* ERR_NO_INIT on database error
|
||||
*/
|
||||
static int32_t QuerySubCommandPermission(
|
||||
const std::string &toolName,
|
||||
const std::string &subCommand,
|
||||
std::vector<std::string> &permissions);
|
||||
|
||||
/**
|
||||
* @brief Build CommandPermission result object
|
||||
* @param cmd Command
|
||||
* @param permissions Permission list
|
||||
* @param queryRet Query result code
|
||||
* @return CommandPermission object
|
||||
*/
|
||||
static CommandPermission BuildCommandPermission(
|
||||
const Command &cmd,
|
||||
const std::vector<std::string> &permissions,
|
||||
int32_t queryRet);
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
int32_t CreateChildProcess(const ExecToolParam ¶m, const std::string &sandboxConfig,
|
||||
const ToolInfo &toolInfo, std::shared_ptr<SessionRecord> record) const;
|
||||
|
||||
bool TerminateProcess(pid_t pid, int signal = SIGTERM) const;
|
||||
bool Killpg(pid_t pid) const;
|
||||
|
||||
private:
|
||||
ProcessManager() = default;
|
||||
|
||||
@@ -30,10 +30,17 @@
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
enum class SessionType {
|
||||
CLI = 0,
|
||||
SKILL,
|
||||
};
|
||||
|
||||
enum class SessionState {
|
||||
SPAWNING = 0,
|
||||
RUNNING,
|
||||
CANCELLING,
|
||||
COMPLETED,
|
||||
FAILED,
|
||||
};
|
||||
|
||||
class SessionRecord {
|
||||
@@ -51,6 +58,7 @@ public:
|
||||
int32_t stdinPipe[2] = {-1, -1}; // [0]=read, [1]=write
|
||||
int32_t stdoutPipe[2] = {-1, -1};
|
||||
int32_t stderrPipe[2] = {-1, -1};
|
||||
SessionType sessionType = SessionType::CLI;
|
||||
|
||||
void SetState(SessionState state);
|
||||
SessionState GetState() const;
|
||||
@@ -58,6 +66,8 @@ public:
|
||||
void SetTerminalResult(int32_t status, int32_t sig);
|
||||
int32_t GetTerminalStatus() const;
|
||||
|
||||
void SetSkillResult(int32_t resultCode, const std::string &outputText);
|
||||
|
||||
void SetTimedOut(bool timedOut);
|
||||
bool TimedOut() const;
|
||||
int64_t GetEndTimeMs() const;
|
||||
@@ -76,6 +86,11 @@ public:
|
||||
|
||||
void BuildSessionInfo(CliSessionInfo &session) const;
|
||||
|
||||
inline pid_t GetCallerPid()
|
||||
{
|
||||
return callerPid;
|
||||
}
|
||||
|
||||
private:
|
||||
void TrimBufferedOutput(std::string &buffer);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <iremote_object.h>
|
||||
#include <functional>
|
||||
|
||||
#include "cli_session_info.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace AAFwk {
|
||||
class WantParams;
|
||||
@@ -32,6 +34,7 @@ struct IArray;
|
||||
}
|
||||
namespace AppExecFwk {
|
||||
struct BundleInfo;
|
||||
struct SkillExecuteResult;
|
||||
}
|
||||
namespace CliTool {
|
||||
class ExecToolParam;
|
||||
@@ -47,13 +50,17 @@ public:
|
||||
|
||||
static std::string GenerateCliSessionId(const std::string &name, std::shared_ptr<SessionRecord> record);
|
||||
|
||||
static bool GenerateSandboxConfig(const std::string &challenge, AccessToken::AccessTokenID tokenId,
|
||||
std::string &sandboxConfig);
|
||||
static bool GenerateSandboxConfig(const ExecToolParam ¶m, AccessToken::AccessTokenID tokenId,
|
||||
std::string &sandboxConfig, std::string &bundleName);
|
||||
|
||||
static void TransferToCmdParam(const ToolInfo &toolInfo, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
|
||||
// Path utilities (public for testing)
|
||||
static std::vector<std::string> SplitPathBySeparator(const std::string &path, const std::string &separator);
|
||||
static bool IsSkillTool(const std::string &toolName);
|
||||
static void NormalizeSkillParamKeys(AAFwk::WantParams &args);
|
||||
static void ExpandArgsJsonString(AAFwk::WantParams &args);
|
||||
static std::shared_ptr<AAFwk::WantParams> FilterSkillArgs(const AAFwk::WantParams &args);
|
||||
static CliSessionInfo BuildSkillSessionInfo(const std::string &sessionId,
|
||||
int32_t resultCode, const AppExecFwk::SkillExecuteResult &skillResult);
|
||||
|
||||
private:
|
||||
static bool GetBundleInfoByTokenId(AccessToken::AccessTokenID tokenId,
|
||||
@@ -63,12 +70,8 @@ private:
|
||||
// Helper methods for type validation
|
||||
static bool ValidateParamType(const sptr<AAFwk::IInterface> &value, const std::string &expectedType,
|
||||
const nlohmann::json &propertySchema, const std::string &key = "");
|
||||
static bool ValidateNestedObject(const AAFwk::WantParams &nestedParams,
|
||||
const nlohmann::json &objectSchema, const std::string &parentKey);
|
||||
static bool ValidateArrayType(const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json &propertySchema, const std::string &key);
|
||||
static bool ValidateObjectType(const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json &propertySchema, const std::string &key);
|
||||
static bool ValidateArrayItems(sptr<AAFwk::IArray> arrayObj,
|
||||
const nlohmann::json &itemsSchema, const std::string &key);
|
||||
static bool ValidateBasicType(const sptr<AAFwk::IInterface> &value, const std::string &expectedType);
|
||||
@@ -77,86 +80,22 @@ private:
|
||||
static bool IsIntegerType(const sptr<AAFwk::IInterface> &value);
|
||||
static bool IsNumberType(const sptr<AAFwk::IInterface> &value);
|
||||
static bool IsArrayType(const sptr<AAFwk::IInterface> &value);
|
||||
static bool IsObjectType(const sptr<AAFwk::IInterface> &value);
|
||||
|
||||
// Helper methods for argument mapping
|
||||
static void ApplyFlagMapping(const std::string &templates, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
static void ApplyPositionalMapping(const std::string &order, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
static void ApplyFlattenedMapping(const std::string &separator, const std::string &templates,
|
||||
const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
static void ApplyJsonStringMapping(const std::string &templates, const AAFwk::WantParams &args,
|
||||
std::string &cmdLine);
|
||||
static void ApplyMixedMapping(const std::string &templates, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
static std::string FormatTemplate(const std::string &tmpl, const std::string &value);
|
||||
// Helper methods for args expansion (extracted to reduce nesting depth)
|
||||
static bool ExpandArgsFromJson(AAFwk::WantParams &args, const std::string &argsStr);
|
||||
static void ExpandArgsFromWantParams(AAFwk::WantParams &args);
|
||||
|
||||
// Helper methods for mode processing (extracted to reduce nesting depth)
|
||||
static void ProcessPositionalMode(const sptr<AAFwk::IInterface> &value, const nlohmann::json ¶mConfig,
|
||||
std::vector<std::pair<int, std::string>> &positionalParams);
|
||||
static void ProcessFlattenedMode(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json ¶mConfig, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
static void ProcessArrayExpansion(const sptr<AAFwk::IInterface> &value, const std::string &tmpl,
|
||||
static void ProcessBooleanParam(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
std::string &cmdLine);
|
||||
static void ProcessJsonStringTemplate(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json &templateValue, std::string &cmdLine);
|
||||
static void ProcessBooleanTemplate(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json &templateValue, std::string &cmdLine);
|
||||
static void ProcessFlattenedTemplate(const std::string &flattenedKey, const nlohmann::json &templateValue,
|
||||
const std::string &separator, const AAFwk::WantParams &args, std::string &cmdLine);
|
||||
|
||||
// JSON conversion helper
|
||||
static std::string ConvertValueToJson(const std::string &key, const sptr<AAFwk::IInterface> &value);
|
||||
|
||||
// Nested path query helper for flattened mapping
|
||||
static sptr<AAFwk::IInterface> QueryNestedValue(const AAFwk::WantParams &args,
|
||||
const std::string &path, const std::string &separator);
|
||||
|
||||
// Helper method for nested path traversal
|
||||
static sptr<AAFwk::IInterface> QueryNestedPath(const AAFwk::WantParams &args,
|
||||
const std::vector<std::string> &pathSegments, const std::string &separator);
|
||||
static sptr<AAFwk::IInterface> QueryNextLevel(const sptr<AAFwk::IInterface> ¤tValue,
|
||||
const std::string &nextSegment, const std::string &separator);
|
||||
|
||||
// Helper methods for path query (extracted to reduce QueryNestedValue length)
|
||||
static sptr<AAFwk::IInterface> TryDirectLookup(const AAFwk::WantParams &args,
|
||||
const std::string &path);
|
||||
static sptr<AAFwk::IInterface> TryNestedPathTraversal(const AAFwk::WantParams &args,
|
||||
const std::string &path, const std::string &separator);
|
||||
|
||||
// WantParams to JSON conversion helper for nested objects
|
||||
static std::string WantParamsToJson(const AAFwk::WantParams &wantParams);
|
||||
|
||||
static void ApplyFlattenedModeToSingleParam(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
const std::string &separator, const nlohmann::json &templateValue, const AAFwk::WantParams &args,
|
||||
static void ProcessArrayExpansion(const std::string &key, const sptr<AAFwk::IInterface> &value,
|
||||
std::string &cmdLine);
|
||||
|
||||
// Core parameter processing logic (extracted for reuse)
|
||||
static void ApplyFlagModeLogic(const sptr<AAFwk::IInterface> &value,
|
||||
const nlohmann::json &templateValue, std::string &cmdLine);
|
||||
|
||||
// Type conversion helpers
|
||||
// GetParamStringValue: only supports basic types (bool, int, long, float, double, string)
|
||||
// GetParamArrayValue: supports single-level arrays with basic type elements
|
||||
// GetParamJsonValue: converts to JSON format (supports single-level arrays)
|
||||
// Note: Nested arrays and byte/char/short types are not supported
|
||||
static std::string GetParamStringValue(const sptr<AAFwk::IInterface> &value);
|
||||
static std::string GetParamJsonValue(const sptr<AAFwk::IInterface> &value);
|
||||
static bool GetParamBoolValue(const sptr<AAFwk::IInterface> &value, bool &result);
|
||||
static bool GetParamArrayValue(const sptr<AAFwk::IInterface> &value, std::vector<std::string> &result);
|
||||
|
||||
// Low-level helper methods for code reuse
|
||||
static bool ExtractWantParams(const sptr<AAFwk::IInterface> &value, AAFwk::WantParams &wantParams);
|
||||
static std::string EscapeJsonString(const std::string &str);
|
||||
static void IterateIArray(sptr<AAFwk::IArray> arrayObj,
|
||||
std::function<void(const sptr<AAFwk::IInterface>&)> elementHandler);
|
||||
static std::string BuildJsonArrayFromIArray(sptr<AAFwk::IArray> arrayObj,
|
||||
std::function<std::string(const sptr<AAFwk::IInterface>&)> elementConverter);
|
||||
|
||||
// Type-specific JSON conversion helpers (extracted to reduce GetParamJsonValue length)
|
||||
static std::string ConvertWantParamsToJson(const sptr<AAFwk::IInterface> &value);
|
||||
static std::string ConvertArrayToJson(const sptr<AAFwk::IInterface> &value);
|
||||
static std::string ConvertStringToJson(const sptr<AAFwk::IInterface> &value);
|
||||
static std::string ConvertBooleanToJson(const sptr<AAFwk::IInterface> &value);
|
||||
static std::string ConvertNumericToJson(const sptr<AAFwk::IInterface> &value);
|
||||
};
|
||||
|
||||
} // namespace CliTool
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "cli_tool_app_state_observer.h"
|
||||
|
||||
#include "hilog_tag_wrapper.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
|
||||
CliToolAppStateObserver::CliToolAppStateObserver(const std::string &bundleName, ProcessDiedCallback callback)
|
||||
: bundleName_(bundleName), processDiedCallback_(callback)
|
||||
{}
|
||||
|
||||
void CliToolAppStateObserver::OnProcessDied(const AppExecFwk::ProcessData &processData)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Process died: bundleName=%{public}s, pid=%{public}d",
|
||||
bundleName_.c_str(), processData.pid);
|
||||
|
||||
if (processDiedCallback_) {
|
||||
processDiedCallback_(bundleName_, processData.pid);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -22,18 +22,16 @@
|
||||
#include <set>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "arg_mapping.h"
|
||||
#include "cli_error_code.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t ERR_OK = 0;
|
||||
constexpr int32_t ERR_NO_INIT = -1;
|
||||
constexpr int32_t ERR_FILE_NOT_FOUND = -2;
|
||||
constexpr int32_t ERR_JSON_PARSE_FAILED = -3;
|
||||
constexpr int32_t ERR_KVSTORE_NOT_READY = -4;
|
||||
constexpr int32_t ERR_NAME_NOT_FOUND = -5;
|
||||
constexpr int32_t CHECK_INTERVAL = 100000; // 100ms
|
||||
constexpr int32_t MAX_TIMES = 5; // 5 * 100ms = 500ms
|
||||
|
||||
@@ -61,6 +59,7 @@ CliToolDataManager::CliToolDataManager()
|
||||
CliToolDataManager::~CliToolDataManager()
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "CliToolDataManager destructor called");
|
||||
std::lock_guard<std::mutex> lock(kvStorePtrMutex_);
|
||||
if (kvStorePtr_ != nullptr) {
|
||||
dataManager_.CloseKvStore(APP_ID, kvStorePtr_);
|
||||
}
|
||||
@@ -296,6 +295,17 @@ int32_t CliToolDataManager::GetAllTools(std::vector<ToolInfo> &tools)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolDataManager::GetAllToolsRawData(ToolsRawData &rawData)
|
||||
{
|
||||
std::vector<ToolInfo> tools;
|
||||
int32_t ret = GetAllTools(tools);
|
||||
if (ret != ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
ToolsRawData::FromToolInfoVec(tools, rawData);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolDataManager::GetToolByName(const std::string &name, ToolInfo &tool)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "GetToolByName called: %{public}s", name.c_str());
|
||||
@@ -312,7 +322,7 @@ int32_t CliToolDataManager::GetToolByName(const std::string &name, ToolInfo &too
|
||||
TAG_LOGE(AAFwkTag::SER_ROUTER, "GetToolByName error: %{public}d", status);
|
||||
if (status == DistributedKv::Status::KEY_NOT_FOUND) {
|
||||
TAG_LOGW(AAFwkTag::SER_ROUTER, "key not found");
|
||||
return ERR_NAME_NOT_FOUND;
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
RestoreKvStore(status);
|
||||
return status;
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "ability_manager_client.h"
|
||||
#include "accesstoken_kit.h"
|
||||
#include "app_mgr_client.h"
|
||||
#include "ccm_util.h"
|
||||
#include "cli_error_code.h"
|
||||
#include "cli_tool_app_state_observer.h"
|
||||
#include "event_dispatcher.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include "iexec_tool_callback.h"
|
||||
#include "if_system_ability_manager.h"
|
||||
#include "ipc_skeleton.h"
|
||||
#include "iservice_registry.h"
|
||||
#include "permission_query_util.h"
|
||||
#include "permission_util.h"
|
||||
#include "process_manager.h"
|
||||
#include "session_record.h"
|
||||
@@ -43,6 +46,9 @@ constexpr int32_t QUERY_SUCCESS = 0;
|
||||
constexpr int32_t QUERY_COMMAND_NOT_EXIST = 1;
|
||||
constexpr int32_t QUERY_DB_ERROR = 2;
|
||||
constexpr int32_t MAX_QUERY_CMDS_SIZE = 100;
|
||||
constexpr int32_t ACTIVE_TIME = 30 * 1000; // 30s
|
||||
constexpr int32_t SKILL_TYPE_INDEPENDENT = -1;
|
||||
sptr<SkillCallbackAdapter> adaptor_;
|
||||
} // namespace
|
||||
|
||||
std::mutex g_mutex;
|
||||
@@ -61,6 +67,7 @@ sptr<CliToolManagerService> CliToolManagerService::GetInstance()
|
||||
|
||||
int32_t CliToolManagerService::RegisterScheduler(const sptr<ICliToolManagerScheduler> &scheduler)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
if (EventDispatcher::GetInstance().RegisterScheduler(IPCSkeleton::GetCallingPid(), scheduler)) {
|
||||
return ERR_OK;
|
||||
}
|
||||
@@ -69,6 +76,7 @@ int32_t CliToolManagerService::RegisterScheduler(const sptr<ICliToolManagerSched
|
||||
|
||||
int32_t CliToolManagerService::UnregisterScheduler()
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
EventDispatcher::GetInstance().UnregisterScheduler(IPCSkeleton::GetCallingPid());
|
||||
return ERR_OK;
|
||||
}
|
||||
@@ -81,6 +89,12 @@ void CliToolManagerService::HandleProcessTimeout(const std::string &sessionId)
|
||||
"HandleProcessTimeout skipped: sessionId=%{public}s not found", sessionId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (record->sessionType == SessionType::SKILL) {
|
||||
HandleSkillSessionTimeout(sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "HandleProcessTimeout: sessionId=%{public}s", sessionId.c_str());
|
||||
record->SetTimedOut(true);
|
||||
record->SetState(SessionState::CANCELLING);
|
||||
@@ -96,7 +110,7 @@ void CliToolManagerService::HandleProcessTimeout(const std::string &sessionId)
|
||||
}
|
||||
|
||||
EventDispatcher::GetInstance().DispatchErrorEvent(sessionId, "session timed out");
|
||||
ProcessManager::GetInstance().TerminateProcess(record->processId, SIGKILL);
|
||||
ProcessManager::GetInstance().Killpg(record->processId);
|
||||
}
|
||||
|
||||
void CliToolManagerService::HandleProcessYieldTimeout(const std::string &sessionId)
|
||||
@@ -244,6 +258,8 @@ void CliToolManagerService::OnStart()
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Publish failed");
|
||||
return;
|
||||
}
|
||||
DelayUnloadTask();
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "climgr start success");
|
||||
}
|
||||
|
||||
void CliToolManagerService::OnStop()
|
||||
@@ -254,7 +270,7 @@ void CliToolManagerService::OnStop()
|
||||
// Collect active PIDs before clearing sessions
|
||||
std::vector<pid_t> activePids;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessionsMutex_);
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
for (const auto &[sessionId, record] : sessionRecords_) {
|
||||
if (record != nullptr && record->processId > 0) {
|
||||
activePids.push_back(record->processId);
|
||||
@@ -269,7 +285,7 @@ void CliToolManagerService::OnStop()
|
||||
// Kill all active processes
|
||||
auto &processManager = ProcessManager::GetInstance();
|
||||
for (pid_t pid : activePids) {
|
||||
processManager.TerminateProcess(pid, SIGKILL);
|
||||
processManager.Killpg(pid);
|
||||
}
|
||||
|
||||
if (ioMonitor_ != nullptr) {
|
||||
@@ -277,29 +293,84 @@ void CliToolManagerService::OnStop()
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::OnIdle(const SystemAbilityOnDemandReason &idlReason)
|
||||
{
|
||||
int32_t sessionSize = 0;
|
||||
{
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
sessionSize = static_cast<int32_t>(sessionRecords_.size());
|
||||
}
|
||||
int32_t calledCount = interfaceCalledCount_.load();
|
||||
if (calledCount != 0 || sessionSize != 0) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "service busy, calledCount=%{public}d, sessionSize=%{public}d",
|
||||
calledCount, sessionSize);
|
||||
if (!CancelIdle()) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "Fail to cancel idle");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CliToolManagerService::AddSessionRecord(const std::shared_ptr<SessionRecord> &record)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessionsMutex_);
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
sessionRecords_[record->sessionId] = record;
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRecord> CliToolManagerService::GetSessionRecord(const std::string &sessionId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessionsMutex_);
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
auto it = sessionRecords_.find(sessionId);
|
||||
if (it == sessionRecords_.end()) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "GetSessionRecord failed: sessionId=%{public}s not found", sessionId.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
if (it->second == nullptr) {
|
||||
sessionRecords_.erase(it); // for leak
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void CliToolManagerService::RemoveSessionRecord(const std::string &sessionId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessionsMutex_);
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
sessionRecords_.erase(sessionId);
|
||||
}
|
||||
|
||||
void CliToolManagerService::DelayUnloadTask()
|
||||
{
|
||||
auto task = []() {
|
||||
int32_t sessionSize = 0;
|
||||
{
|
||||
std::lock_guard<ffrt::mutex> guard(CliToolManagerService::GetInstance()->sessionsMutex_);
|
||||
sessionSize = static_cast<int32_t>(CliToolManagerService::GetInstance()->sessionRecords_.size());
|
||||
}
|
||||
int32_t calledCount = CliToolManagerService::GetInstance()->interfaceCalledCount_.load();
|
||||
if (calledCount == 0 && sessionSize == 0) {
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "UnloadSA start");
|
||||
sptr<ISystemAbilityManager> saManager =
|
||||
OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
|
||||
if (saManager == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "null saManager");
|
||||
return;
|
||||
}
|
||||
int32_t result = saManager->UnloadSystemAbility(CLI_TOOL_MGR_SERVICE_ID);
|
||||
if (result != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "UnloadSystemAbility ret: %{public}d", result);
|
||||
return;
|
||||
}
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "UnloadSA success");
|
||||
} else {
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Service still busy (calledCount=%{public}d, sessionSize=%{public}d), "
|
||||
"reschedule delay unload task", calledCount, sessionSize);
|
||||
CliToolManagerService::GetInstance()->DelayUnloadTask();
|
||||
}
|
||||
};
|
||||
ffrt::submit(std::move(task), ffrt::task_attr().delay(ACTIVE_TIME * COEFFICIENT));
|
||||
}
|
||||
|
||||
bool CliToolManagerService::RegisterSessionWithMonitors(const std::shared_ptr<SessionRecord> &record,
|
||||
const ExecToolParam ¶m)
|
||||
{
|
||||
@@ -329,10 +400,10 @@ void CliToolManagerService::UnregisterSessionWithMonitors(const std::string &ses
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::GetAllToolInfos(std::vector<ToolInfo> &tools)
|
||||
int32_t CliToolManagerService::GetAllToolInfos(ToolsRawData &tools)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "GetAllToolInfos called");
|
||||
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "GetAllToolInfos: Not system app");
|
||||
@@ -345,13 +416,13 @@ int32_t CliToolManagerService::GetAllToolInfos(std::vector<ToolInfo> &tools)
|
||||
return ERR_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
return CliToolDataManager::GetInstance().GetAllTools(tools);
|
||||
return CliToolDataManager::GetInstance().GetAllToolsRawData(tools);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::GetAllToolSummaries(std::vector<ToolSummary> &summaries)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "GetAllToolSummaries called");
|
||||
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "GetAllToolSummaries: Not system app");
|
||||
@@ -370,7 +441,7 @@ int32_t CliToolManagerService::GetAllToolSummaries(std::vector<ToolSummary> &sum
|
||||
int32_t CliToolManagerService::GetToolInfoByName(const std::string &name, ToolInfo &tool)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "GetToolInfoByName called, name='%{public}s'", name.c_str());
|
||||
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "GetToolInfoByName: Not system app");
|
||||
@@ -389,14 +460,11 @@ int32_t CliToolManagerService::GetToolInfoByName(const std::string &name, ToolIn
|
||||
int32_t CliToolManagerService::RegisterTool(const ToolInfo &tool)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "RegisterTool called, tool name='%{public}s'", tool.name.c_str());
|
||||
return CliToolDataManager::GetInstance().RegisterTool(tool);
|
||||
return ERR_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ExecTool(const ExecToolParam ¶m, const std::string &eventId)
|
||||
int32_t CliToolManagerService::ValidateExecToolPermissions()
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "ExecTool called: toolName=%{public}s, subcommand=%{public}s",
|
||||
param.toolName.c_str(), param.subcommand.c_str());
|
||||
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -407,15 +475,24 @@ int32_t CliToolManagerService::ExecTool(const ExecToolParam ¶m, const std::s
|
||||
if (!PermissionUtil::VerifyAccessToken(tokenId, PERMISSION_EXEC_CLI_TOOL)) {
|
||||
return ERR_PERMISSION_DENIED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ValidateSessionLimit()
|
||||
{
|
||||
auto cliQuantity = CcmUtil::GetInstance().GetCliConcurrencyLimit();
|
||||
if (activeSessionCount_.load() >= cliQuantity) {
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
if (static_cast<int32_t>(sessionRecords_.size()) >= cliQuantity) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Session limit exceeded: %{public}d", cliQuantity);
|
||||
return ERR_SESSION_LIMIT_EXCEEDED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
ToolInfo toolInfo;
|
||||
if (GetToolInfoByName(param.toolName, toolInfo) != ERR_OK) {
|
||||
int32_t CliToolManagerService::ValidateAndPrepareTool(const ExecToolParam ¶m, uint32_t tokenId,
|
||||
ToolInfo &toolInfo, std::string &sandboxConfig, std::string &bundleName)
|
||||
{
|
||||
if (CliToolDataManager::GetInstance().GetToolByName(param.toolName, toolInfo) != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool not found");
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
@@ -426,45 +503,102 @@ int32_t CliToolManagerService::ExecTool(const ExecToolParam ¶m, const std::s
|
||||
return checkPramRet;
|
||||
}
|
||||
|
||||
std::string sandboxConfig;
|
||||
if (!ToolUtil::GenerateSandboxConfig(param.challenge, tokenId, sandboxConfig)) {
|
||||
if (!ToolUtil::GenerateSandboxConfig(param, tokenId, sandboxConfig, bundleName)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "caller is not hap");
|
||||
return ERR_NOT_HAP;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
// Create and initialize session record
|
||||
std::shared_ptr<SessionRecord> record = CreateSessionRecord(param);
|
||||
int32_t CliToolManagerService::SetupAndStartSession(const ExecToolParam ¶m, const std::string &eventId,
|
||||
const ToolInfo &toolInfo, const std::string &sandboxConfig, const std::string &bundleName)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL,
|
||||
"Dispatch to CLI path, toolName=%{public}s eventId=%{public}s", param.toolName.c_str(), eventId.c_str());
|
||||
|
||||
std::shared_ptr<SessionRecord> record = CreateSessionRecord(param, eventId);
|
||||
if (record == nullptr) {
|
||||
return ERR_NO_INIT;
|
||||
}
|
||||
record->eventId = eventId;
|
||||
|
||||
auto createRet = ProcessManager::GetInstance().CreateChildProcess(param, sandboxConfig, toolInfo, record);
|
||||
if (createRet != ERR_OK) {
|
||||
return createRet;
|
||||
}
|
||||
activeSessionCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
AddSessionRecord(record);
|
||||
|
||||
if (RegisterSessionWithMonitors(record, param) == false) {
|
||||
ProcessManager::GetInstance().TerminateProcess(record->processId, SIGKILL);
|
||||
ProcessManager::GetInstance().Killpg(record->processId);
|
||||
RemoveSessionRecord(record->sessionId);
|
||||
return ERR_NO_INIT;
|
||||
}
|
||||
|
||||
// Background session, immediately notify session info
|
||||
if (param.options.background) {
|
||||
CliSessionInfo session;
|
||||
record->BuildSessionInfo(session);
|
||||
EventDispatcher::GetInstance().DispatchExecToolReplyEvent(record->callerPid, eventId, ERR_OK, session);
|
||||
if (!bundleName.empty()) {
|
||||
RegisterAppStateObserver(bundleName, record->callerPid);
|
||||
}
|
||||
|
||||
if (param.options.background) {
|
||||
HandleBackgroundSessionReply(record, eventId);
|
||||
}
|
||||
|
||||
// Frontground session, after waiting for the yieldMs timeout, notify the session info
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void CliToolManagerService::PostExecToolTask(int32_t time, const std::string &sessionId, bool isTimeout)
|
||||
void CliToolManagerService::HandleBackgroundSessionReply(
|
||||
const std::shared_ptr<SessionRecord> &record, const std::string &eventId)
|
||||
{
|
||||
CliSessionInfo session;
|
||||
record->BuildSessionInfo(session);
|
||||
EventDispatcher::GetInstance().DispatchExecToolReplyEvent(record->callerPid, eventId, ERR_OK, session);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ExecTool(const ExecToolParam ¶m, const std::string &eventId)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "ExecTool called: toolName=%{public}s, subcommand=%{public}s",
|
||||
param.toolName.c_str(), param.subcommand.c_str());
|
||||
ToolInfo toolInfo;
|
||||
if (ToolUtil::IsSkillTool(param.toolName)) {
|
||||
int32_t skillType = 0;
|
||||
auto skillRet = ValidateSkillTypeFromParam(param, skillType);
|
||||
if (skillRet == ERR_OK && skillType != SKILL_TYPE_INDEPENDENT) {
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL,
|
||||
"Dispatch to skill path, toolName=%{public}s eventId=%{public}s",
|
||||
param.toolName.c_str(), eventId.c_str());
|
||||
int32_t ret = SetupAndStartSkillSession(param, eventId, toolInfo);
|
||||
if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"Skill dispatch failed, toolName=%{public}s ret=%{public}d", param.toolName.c_str(), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if (skillRet != ERR_OK) {
|
||||
return skillRet;
|
||||
}
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL,
|
||||
"Independent skill, fallback to CLI path, toolName=%{public}s", param.toolName.c_str());
|
||||
}
|
||||
|
||||
if (auto ret = ValidateExecToolPermissions(); ret != ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (auto ret = ValidateSessionLimit(); ret != ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto tokenId = IPCSkeleton::GetCallingTokenID();
|
||||
|
||||
std::string sandboxConfig;
|
||||
std::string bundleName;
|
||||
|
||||
if (auto ret = ValidateAndPrepareTool(param, tokenId, toolInfo, sandboxConfig, bundleName); ret != ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return SetupAndStartSession(param, eventId, toolInfo, sandboxConfig, bundleName);
|
||||
}
|
||||
|
||||
void CliToolManagerService::PostExecToolTask(int64_t time, const std::string &sessionId, bool isTimeout)
|
||||
{
|
||||
auto timeoutTask = [sessionId, isTimeout]() {
|
||||
auto service = CliToolManagerService::GetInstance();
|
||||
@@ -483,15 +617,23 @@ void CliToolManagerService::WaitPid(pid_t pid, int32_t status, int32_t sig)
|
||||
{
|
||||
std::shared_ptr<SessionRecord> record = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessionsMutex_);
|
||||
for (auto iter = sessionRecords_.begin(); iter != sessionRecords_.end(); ++iter) {
|
||||
if (iter->second == nullptr || pid != iter->second->processId) {
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
for (auto iter = sessionRecords_.begin(); iter != sessionRecords_.end();) {
|
||||
if (iter->second == nullptr) {
|
||||
std::string sessionId = iter->first;
|
||||
iter = sessionRecords_.erase(iter);
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "delete leak sessionId:%{public}s", sessionId.c_str());
|
||||
continue;
|
||||
}
|
||||
record = iter->second;
|
||||
break;
|
||||
if (pid == iter->second->processId) {
|
||||
record = iter->second;
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
AccessToken::AccessTokenKit::DeleteToolTokenByPid(pid);
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "WaitPid delete tool pid:%{public}d", pid);
|
||||
if (record) {
|
||||
record->SetTerminalResult(status, sig);
|
||||
if (record->OutputDrained()) {
|
||||
@@ -508,36 +650,105 @@ void CliToolManagerService::sigchld_handler(int32_t sig)
|
||||
auto instance = CliToolManagerService::GetInstance();
|
||||
if (instance != nullptr) {
|
||||
instance->WaitPid(pid, status, sig);
|
||||
ProcessManager::GetInstance().Killpg(pid);
|
||||
}
|
||||
pid_t gPid = getpgid(pid);
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "gPid=%{public}d", gPid);
|
||||
if (gPid == -1) {
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Fial to get gPid");
|
||||
return;
|
||||
}
|
||||
int32_t killRet = killpg(gPid, SIGTERM);
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "killpg result:%{public}d", killRet);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRecord> CliToolManagerService::CreateSessionRecord(const ExecToolParam ¶m)
|
||||
void CliToolManagerService::OnProcessDied(const std::string &bundleName, pid_t diedPid)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "OnProcessDied called: bundleName=%{public}s, diedPid=%{public}d",
|
||||
bundleName.c_str(), diedPid);
|
||||
std::lock_guard<ffrt::mutex> guard(sessionsMutex_);
|
||||
// Iterate through sessionRecords_ to find matching SessionRecord by callerPid
|
||||
for (auto iter = sessionRecords_.begin(); iter != sessionRecords_.end();) {
|
||||
auto sessionRecord = iter->second;
|
||||
if (sessionRecord == nullptr) {
|
||||
std::string sessionId = iter->first;
|
||||
iter = sessionRecords_.erase(iter);
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "delete leak sessionId:%{public}s", sessionId.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this session's callerPid matches the diedPid
|
||||
if (sessionRecord->GetCallerPid() != diedPid) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Kill the CLI process group (skill sessions have processId=-1, Killpg handles it)
|
||||
if (sessionRecord->processId > 0) {
|
||||
ProcessManager::GetInstance().Killpg(sessionRecord->processId);
|
||||
}
|
||||
|
||||
// Clean up session
|
||||
iter = sessionRecords_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void CliToolManagerService::RegisterAppStateObserver(const std::string &bundleName, pid_t callerPid)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "RegisterAppStateObserver called: bundleName=%{public}s, callerPid=%{public}d",
|
||||
bundleName.c_str(), callerPid);
|
||||
|
||||
// Check if observer already exists for this bundle
|
||||
if (bundleObservers_.find(bundleName) != bundleObservers_.end()) {
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Observer already registered for bundleName=%{public}s", bundleName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Create observer with callback to OnProcessDied
|
||||
auto callback = [](const std::string &bundleName, pid_t diedPid) {
|
||||
auto service = CliToolManagerService::GetInstance();
|
||||
if (service != nullptr) {
|
||||
service->OnProcessDied(bundleName, diedPid);
|
||||
}
|
||||
};
|
||||
|
||||
sptr<CliToolAppStateObserver> observer = new CliToolAppStateObserver(bundleName, callback);
|
||||
if (observer == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to create observer for bundleName=%{public}s", bundleName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Register observer through AppMgrClient
|
||||
AppExecFwk::AppMgrClient appMgrClient;
|
||||
auto ret = appMgrClient.ConnectAppMgrService();
|
||||
if (ret != AppExecFwk::AppMgrResultCode::RESULT_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to connect to AppMgrService");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> bundleNameList = { bundleName };
|
||||
auto registerRet = appMgrClient.RegisterApplicationStateObserver(observer, bundleNameList);
|
||||
if (registerRet != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to register observer for bundleName=%{public}s, ret=%{public}d",
|
||||
bundleName.c_str(), registerRet);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store observer
|
||||
bundleObservers_[bundleName] = observer;
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Successfully registered observer for bundleName=%{public}s", bundleName.c_str());
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRecord> CliToolManagerService::CreateSessionRecord(const ExecToolParam ¶m,
|
||||
const std::string &eventId)
|
||||
{
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
if (record == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t timeoutMs = param.options.timeout * COEFFICIENT;
|
||||
record->callerPid = IPCSkeleton::GetCallingPid();
|
||||
record->sessionId = ToolUtil::GenerateCliSessionId(param.toolName, record);
|
||||
record->toolName = param.toolName;
|
||||
record->timeoutMs = timeoutMs;
|
||||
record->timeoutMs = param.options.timeout * COEFFICIENT;
|
||||
record->SetState(SessionState::RUNNING);
|
||||
record->SetBackground(param.options.background);
|
||||
record->eventId = eventId;
|
||||
return record;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ClearSession(const std::string &sessionId)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -562,8 +773,18 @@ int32_t CliToolManagerService::ClearSession(const std::string &sessionId)
|
||||
}
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "ClearSession: sessionId=%{public}s, pid=%{public}d",
|
||||
sessionId.c_str(), record->processId);
|
||||
if (!ProcessManager::GetInstance().TerminateProcess(record->processId, SIGTERM)) {
|
||||
return ERR_PERMISSION_DENIED;
|
||||
|
||||
if (record->sessionType == SessionType::SKILL) {
|
||||
// Skill sessions don't have a child process, mark as cancelling and clean up
|
||||
record->SetState(SessionState::CANCELLING);
|
||||
EventDispatcher::GetInstance().DispatchExitEvent(sessionId, 0);
|
||||
EventDispatcher::GetInstance().ClearSessionSubscribers(sessionId);
|
||||
RemoveSessionRecord(sessionId);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
if (!ProcessManager::GetInstance().Killpg(record->processId)) {
|
||||
return ERR_NOT_KILL;
|
||||
}
|
||||
record->SetState(SessionState::CANCELLING);
|
||||
return ERR_OK;
|
||||
@@ -571,6 +792,7 @@ int32_t CliToolManagerService::ClearSession(const std::string &sessionId)
|
||||
|
||||
int32_t CliToolManagerService::SubscribeSession(const std::string &sessionId, const std::string &subscriptionId)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -588,12 +810,21 @@ int32_t CliToolManagerService::SubscribeSession(const std::string &sessionId, co
|
||||
sessionId.c_str(), subscriptionId.c_str());
|
||||
return ERR_INVALID_PARAM;
|
||||
}
|
||||
if (GetSessionRecord(sessionId) == nullptr) {
|
||||
auto record = GetSessionRecord(sessionId);
|
||||
if (record == nullptr) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"SubscribeSession failed: sessionId=%{public}s not found, subscriptionId=%{public}s",
|
||||
sessionId.c_str(), subscriptionId.c_str());
|
||||
return ERR_CLI_SESSION_NOT_FOUND;
|
||||
}
|
||||
CliSessionInfo session;
|
||||
record->BuildSessionInfo(session);
|
||||
if (session.status != "running") {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"SubscribeSession failed: sessionId=%{public}s status=%{public}s is not subscribable",
|
||||
sessionId.c_str(), session.status.c_str());
|
||||
return ERR_CLI_SESSION_NOT_FOUND;
|
||||
}
|
||||
if (!EventDispatcher::GetInstance().RegisterSubscriber(
|
||||
sessionId, subscriptionId, IPCSkeleton::GetCallingPid())) {
|
||||
return ERR_NO_INIT;
|
||||
@@ -603,6 +834,7 @@ int32_t CliToolManagerService::SubscribeSession(const std::string &sessionId, co
|
||||
|
||||
int32_t CliToolManagerService::UnsubscribeSession(const std::string &sessionId, const std::string &subscriptionId)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -623,6 +855,7 @@ int32_t CliToolManagerService::UnsubscribeSession(const std::string &sessionId,
|
||||
|
||||
int32_t CliToolManagerService::QuerySession(const std::string &sessionId, CliSessionInfo &session)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -646,6 +879,7 @@ int32_t CliToolManagerService::QuerySession(const std::string &sessionId, CliSes
|
||||
int32_t CliToolManagerService::SendMessage(const std::string &sessionId,
|
||||
const std::string &inputText, const std::string &eventId)
|
||||
{
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
auto fullTokenId = IPCSkeleton::GetCallingFullTokenID();
|
||||
if (!AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId)) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Not system app");
|
||||
@@ -674,115 +908,206 @@ int32_t CliToolManagerService::SendMessage(const std::string &sessionId,
|
||||
return ERR_CLI_SEND_MESSAGE;
|
||||
}
|
||||
|
||||
// Skill sessions don't support stdin
|
||||
if (record->sessionType == SessionType::SKILL) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"SendMessage failed: sessionId=%{public}s is a skill session (no stdin)", sessionId.c_str());
|
||||
return ERR_CLI_SEND_MESSAGE;
|
||||
}
|
||||
|
||||
ioMonitor_->SendMessage(sessionId, inputText, eventId);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::BatchQueryPermissionBySubCommand(const std::vector<Command> &cmds,
|
||||
int32_t CliToolManagerService::BatchQueryPermissionBySubCommand(
|
||||
const std::vector<Command> &cmds,
|
||||
std::vector<CommandPermission> &cmdPermissions)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "BatchQueryPermissionBySubCommand called, count=%{public}zu", cmds.size());
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "BatchQueryPermissionBySubCommand begin, cnt=%{public}zu", cmds.size());
|
||||
InterfaceCallCounter counter(interfaceCalledCount_);
|
||||
if (cmds.empty() || cmds.size() >= MAX_QUERY_CMDS_SIZE) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Commands is empty or reach limit");
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "cmds is empty or reach limit");
|
||||
return ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
Security::AccessToken::AccessTokenID callerToken = IPCSkeleton::GetCallingTokenID();
|
||||
Security::AccessToken::ATokenTypeEnum tokenType =
|
||||
Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(callerToken);
|
||||
if (tokenType != Security::AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
|
||||
auto callerToken = IPCSkeleton::GetCallingTokenID();
|
||||
if (Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(callerToken) !=
|
||||
Security::AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Caller is not SA");
|
||||
return ERR_NOT_SA_CALLER;
|
||||
}
|
||||
|
||||
int ret = Security::AccessToken::AccessTokenKit::VerifyAccessToken(callerToken, "ohos.permission.QUERY_CLI_TOOL");
|
||||
if (ret != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Permission denied: ohos.permission.QUERY_CLI_TOOL");
|
||||
if (!PermissionUtil::VerifyAccessToken(callerToken, PERMISSION_QUERY_CLI_TOOL)) {
|
||||
return ERR_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "BatchQueryPermissionBySubCommand begin");
|
||||
cmdPermissions.clear();
|
||||
cmdPermissions.reserve(cmds.size());
|
||||
return PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions);
|
||||
}
|
||||
|
||||
for (const auto &cmd : cmds) {
|
||||
CommandPermission cmdPerm;
|
||||
cmdPerm.cmd = cmd;
|
||||
cmdPerm.permissions.clear();
|
||||
if (cmd.toolName.empty()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool name is empty");
|
||||
return ERR_INVALID_PARAM;
|
||||
}
|
||||
SkillCallbackAdapter::SkillCallbackAdapter(const std::string &sessionId,
|
||||
int32_t callerPid, const std::string &eventId)
|
||||
: sessionId_(sessionId), callerPid_(callerPid), eventId_(eventId)
|
||||
{}
|
||||
|
||||
int32_t ret = DoQueryPermission(cmd, cmdPerm.permissions);
|
||||
if (ret == ERR_OK) {
|
||||
cmdPerm.queryRet = QUERY_SUCCESS;
|
||||
} else if (ret == ERR_TOOL_NOT_EXIST) {
|
||||
cmdPerm.queryRet = QUERY_COMMAND_NOT_EXIST;
|
||||
cmdPerm.permissions.clear();
|
||||
} else {
|
||||
// ERR_NO_INIT
|
||||
cmdPerm.queryRet = QUERY_DB_ERROR;
|
||||
cmdPerm.permissions.clear();
|
||||
}
|
||||
cmdPermissions.push_back(std::move(cmdPerm));
|
||||
void SkillCallbackAdapter::OnExecuteDone(const std::string &requestCode, int32_t resultCode,
|
||||
const AppExecFwk::SkillExecuteResult &result)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL,
|
||||
"SkillCallbackAdapter::OnExecuteDone sessionId:%{public}s code:%{public}d",
|
||||
sessionId_.c_str(), resultCode);
|
||||
|
||||
auto service = CliToolManagerService::GetInstance();
|
||||
if (service == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "service expired for sessionId:%{public}s", sessionId_.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Batch query completed, total=%{public}zu", cmdPermissions.size());
|
||||
auto record = service->GetSessionRecord(sessionId_);
|
||||
if (record == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL,
|
||||
"OnExecuteDone skipped: sessionId:%{public}s already cleaned", sessionId_.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string outputText;
|
||||
if (result.result != nullptr) {
|
||||
outputText = result.result->ToString();
|
||||
}
|
||||
record->SetSkillResult(resultCode, outputText);
|
||||
record->SetState(resultCode == ERR_OK ? SessionState::COMPLETED : SessionState::FAILED);
|
||||
|
||||
auto session = ToolUtil::BuildSkillSessionInfo(sessionId_, resultCode, result);
|
||||
service->HandleSkillSessionComplete(sessionId_, callerPid_, eventId_, resultCode, session);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ValidateSkillTypeFromParam(const ExecToolParam ¶m, int32_t &skillType)
|
||||
{
|
||||
auto &args = const_cast<ExecToolParam &>(param).args;
|
||||
ToolUtil::NormalizeSkillParamKeys(args);
|
||||
auto bundleName = args.GetStringParam("bundleName");
|
||||
auto moduleName = args.GetStringParam("moduleName");
|
||||
auto skillName = args.GetStringParam("skillName");
|
||||
if (skillName.empty()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "skillName is required in args");
|
||||
return ERR_INVALID_VALUE;
|
||||
}
|
||||
return ValidateSkillType(bundleName, moduleName, skillName, skillType);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::ValidateSkillType(const std::string &bundleName,
|
||||
const std::string &moduleName, const std::string &skillName, int32_t &skillType)
|
||||
{
|
||||
auto queryRet = AAFwk::AbilityManagerClient::GetInstance()->QuerySkillType(
|
||||
bundleName, moduleName, skillName, skillType);
|
||||
if (queryRet != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "querySkillType failed:%{public}d", queryRet);
|
||||
return queryRet;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::DoQueryPermission(const Command &cmd, std::vector<std::string> &permissions)
|
||||
int32_t CliToolManagerService::SetupAndStartSkillSession(const ExecToolParam ¶m,
|
||||
const std::string &eventId, const ToolInfo &toolInfo)
|
||||
{
|
||||
if (cmd.subCommand.empty()) {
|
||||
return QueryMainCommandPermission(cmd.toolName, permissions);
|
||||
}
|
||||
return QuerySubCommandPermission(cmd.toolName, cmd.subCommand, permissions);
|
||||
}
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "SetupAndStartSkillSession: toolName=%{public}s",
|
||||
param.toolName.c_str());
|
||||
|
||||
int32_t CliToolManagerService::QueryMainCommandPermission(const std::string &toolName,
|
||||
std::vector<std::string> &permissions)
|
||||
{
|
||||
ToolInfo toolInfo;
|
||||
int32_t ret = CliToolDataManager::GetInstance().GetToolByName(toolName, toolInfo);
|
||||
if (ret == ERR_NO_INIT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "DB error when querying tool: %{public}s", toolName.c_str());
|
||||
auto &args = const_cast<ExecToolParam &>(param).args;
|
||||
auto bundleName = args.GetStringParam("bundleName");
|
||||
auto moduleName = args.GetStringParam("moduleName");
|
||||
auto skillName = args.GetStringParam("skillName");
|
||||
auto scriptPath = args.GetStringParam("scriptPath");
|
||||
auto funcName = args.GetStringParam("functionName");
|
||||
|
||||
ToolUtil::ExpandArgsJsonString(args);
|
||||
auto skillArgs = ToolUtil::FilterSkillArgs(args);
|
||||
|
||||
auto record = CreateSessionRecord(param, eventId);
|
||||
if (record == nullptr) {
|
||||
return ERR_NO_INIT;
|
||||
} else if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool not found: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
permissions = toolInfo.requirePermissions;
|
||||
record->sessionType = SessionType::SKILL;
|
||||
AddSessionRecord(record);
|
||||
|
||||
auto callerTokenId = IPCSkeleton::GetCallingTokenID();
|
||||
adaptor_ = sptr<SkillCallbackAdapter>::MakeSptr(
|
||||
record->sessionId, record->callerPid, eventId);
|
||||
|
||||
AppExecFwk::SkillExecuteRequest skillRequest;
|
||||
skillRequest.callerTokenId = callerTokenId;
|
||||
skillRequest.bundleName = bundleName;
|
||||
skillRequest.moduleName = moduleName;
|
||||
skillRequest.skillName = skillName;
|
||||
skillRequest.scriptPath = scriptPath;
|
||||
skillRequest.functionName = funcName;
|
||||
skillRequest.skillArgs = skillArgs;
|
||||
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "execSkill before ExecuteInAppSkillWithTokenId");
|
||||
int32_t ret = AAFwk::AbilityManagerClient::GetInstance()->ExecuteInAppSkillWithTokenId(
|
||||
skillRequest, adaptor_);
|
||||
if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "ExecuteInAppSkillWithTokenId failed:%{public}d", ret);
|
||||
RemoveSessionRecord(record->sessionId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param.options.background) {
|
||||
HandleBackgroundSessionReply(record, eventId);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t CliToolManagerService::QuerySubCommandPermission(const std::string &toolName, const std::string &subCommand,
|
||||
std::vector<std::string> &permissions)
|
||||
void CliToolManagerService::HandleSkillSessionComplete(const std::string &sessionId,
|
||||
int32_t callerPid, const std::string &eventId, int32_t resultCode,
|
||||
const CliSessionInfo &session)
|
||||
{
|
||||
ToolInfo toolInfo;
|
||||
int32_t ret = CliToolDataManager::GetInstance().GetToolByName(toolName, toolInfo);
|
||||
if (ret == ERR_NO_INIT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "DB error when querying tool: %{public}s", toolName.c_str());
|
||||
return ERR_NO_INIT;
|
||||
} else if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool not found: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
auto record = GetSessionRecord(sessionId);
|
||||
if (record == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL,
|
||||
"HandleSkillSessionComplete skipped: sessionId:%{public}s not found", sessionId.c_str());
|
||||
return;
|
||||
}
|
||||
if (!record->BeginCleanup()) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL,
|
||||
"HandleSkillSessionComplete skipped: already cleaning sessionId:%{public}s", sessionId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toolInfo.hasSubCommand) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool has no subcommand: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
auto oldBackground = record->SetBackground(true);
|
||||
if (oldBackground == false) {
|
||||
EventDispatcher::GetInstance().DispatchExecToolReplyEvent(callerPid, eventId, ERR_OK, session);
|
||||
}
|
||||
|
||||
auto it = toolInfo.subcommands.find(subCommand);
|
||||
if (it == toolInfo.subcommands.end()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Subcommand not found: %{public}s.%{public}s",
|
||||
toolName.c_str(), subCommand.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
EventDispatcher::GetInstance().DispatchExitEvent(sessionId, 0);
|
||||
EventDispatcher::GetInstance().ClearSessionSubscribers(sessionId);
|
||||
RemoveSessionRecord(sessionId);
|
||||
}
|
||||
|
||||
void CliToolManagerService::HandleSkillSessionTimeout(const std::string &sessionId)
|
||||
{
|
||||
auto record = GetSessionRecord(sessionId);
|
||||
if (record == nullptr) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL,
|
||||
"HandleSkillSessionTimeout skipped: sessionId:%{public}s not found", sessionId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
permissions = it->second.requirePermissions;
|
||||
return ERR_OK;
|
||||
record->SetTimedOut(true);
|
||||
record->SetState(SessionState::FAILED);
|
||||
|
||||
auto oldBackground = record->SetBackground(true);
|
||||
if (oldBackground == false) {
|
||||
CliSessionInfo session;
|
||||
record->BuildSessionInfo(session);
|
||||
EventDispatcher::GetInstance().DispatchExecToolReplyEvent(
|
||||
record->callerPid, record->eventId, ERR_OK, session);
|
||||
}
|
||||
|
||||
EventDispatcher::GetInstance().DispatchErrorEvent(sessionId, "session timed out");
|
||||
EventDispatcher::GetInstance().DispatchExitEvent(sessionId, 0);
|
||||
EventDispatcher::GetInstance().ClearSessionSubscribers(sessionId);
|
||||
RemoveSessionRecord(sessionId);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "event_dispatcher.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
#include "hilog_tag_wrapper.h"
|
||||
|
||||
@@ -318,4 +319,4 @@ void EventDispatcher::RemoveSubscribersForPidLocked(int32_t callerPid)
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
|
||||
#include "io_monitor.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ffrt.h"
|
||||
@@ -33,8 +37,10 @@ namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t MAX_EVENTS = 16;
|
||||
constexpr int32_t EPOLL_WAIT_MS = 100;
|
||||
constexpr int32_t MAX_RETRIES = 10;
|
||||
constexpr int32_t RETRY_DELAY_MS = 10;
|
||||
constexpr int32_t INPUT_WRITE_POLL_MS = 1000;
|
||||
constexpr int32_t INPUT_WRITE_TIMEOUT_MS = 30 * 1000;
|
||||
constexpr size_t MAX_PENDING_INPUT_BYTES = 4 * 1024 * 1024;
|
||||
constexpr size_t MAX_PENDING_INPUT_MESSAGES = 4096;
|
||||
}
|
||||
|
||||
std::shared_ptr<IOMonitor> IOMonitor::Create()
|
||||
@@ -75,11 +81,27 @@ void IOMonitor::Stop()
|
||||
monitorThread_.join();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
for (const auto &[fd, info] : fdMap_) {
|
||||
close(fd);
|
||||
std::vector<std::pair<std::string, PendingInput>> failedInputs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
for (const auto &[fd, info] : fdMap_) {
|
||||
close(fd);
|
||||
}
|
||||
fdMap_.clear();
|
||||
for (auto &[sessionId, queue] : inputQueues_) {
|
||||
while (!queue.pendingInputs.empty()) {
|
||||
failedInputs.emplace_back(sessionId, std::move(queue.pendingInputs.front()));
|
||||
queue.pendingInputs.pop_front();
|
||||
}
|
||||
queue.pendingBytes = 0;
|
||||
queue.writeTaskRunning = false;
|
||||
}
|
||||
inputQueues_.clear();
|
||||
}
|
||||
|
||||
for (const auto &[sessionId, input] : failedInputs) {
|
||||
NotifyInputReply(sessionId, input.eventId, false);
|
||||
}
|
||||
fdMap_.clear();
|
||||
}
|
||||
|
||||
bool IOMonitor::RegisterSession(const std::string &sessionId, int stdoutFd, int stderrFd, int stdinFd)
|
||||
@@ -129,6 +151,7 @@ bool IOMonitor::RegisterSession(const std::string &sessionId, int stdoutFd, int
|
||||
void IOMonitor::UnregisterSession(const std::string &sessionId)
|
||||
{
|
||||
std::vector<std::pair<int, FdInfo>> fdsToClose;
|
||||
std::vector<PendingInput> failedInputs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
for (auto it = fdMap_.begin(); it != fdMap_.end();) {
|
||||
@@ -139,6 +162,14 @@ void IOMonitor::UnregisterSession(const std::string &sessionId)
|
||||
}
|
||||
++it;
|
||||
}
|
||||
auto queueIt = inputQueues_.find(sessionId);
|
||||
if (queueIt != inputQueues_.end()) {
|
||||
while (!queueIt->second.pendingInputs.empty()) {
|
||||
failedInputs.emplace_back(std::move(queueIt->second.pendingInputs.front()));
|
||||
queueIt->second.pendingInputs.pop_front();
|
||||
}
|
||||
inputQueues_.erase(queueIt);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[fd, info] : fdsToClose) {
|
||||
@@ -147,6 +178,9 @@ void IOMonitor::UnregisterSession(const std::string &sessionId)
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
for (const auto &input : failedInputs) {
|
||||
NotifyInputReply(sessionId, input.eventId, false);
|
||||
}
|
||||
}
|
||||
|
||||
void IOMonitor::SetOutputCallback(OutputCallback callback)
|
||||
@@ -172,6 +206,11 @@ void IOMonitor::SetSessionDrainedCallback(SessionDrainedCallback callback)
|
||||
int IOMonitor::GetStdinFd(const std::string &sessionId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
return GetStdinFdLocked(sessionId);
|
||||
}
|
||||
|
||||
int IOMonitor::GetStdinFdLocked(const std::string &sessionId) const
|
||||
{
|
||||
auto it = fdMap_.begin();
|
||||
while (it != fdMap_.end()) {
|
||||
if (it->second.sessionId == sessionId && it->second.isStdin) {
|
||||
@@ -188,52 +227,77 @@ int IOMonitor::GetStdinFd(const std::string &sessionId)
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void IOMonitor::WriteTask(const std::string &sessionId, const std::string &message, const std::string &eventId)
|
||||
bool IOMonitor::WriteMessage(int fd, const std::string &sessionId, const std::string &message)
|
||||
{
|
||||
int fd = GetStdinFd(sessionId);
|
||||
if (fd < 0) {
|
||||
if (inputReplyCallback_) {
|
||||
inputReplyCallback_(sessionId, eventId, false);
|
||||
}
|
||||
return;
|
||||
if (message.empty()) {
|
||||
return true;
|
||||
}
|
||||
bool result = true;
|
||||
const char* data = message.c_str();
|
||||
size_t totalBytes = message.size();
|
||||
size_t bytesWritten = 0;
|
||||
int retryCount = 0;
|
||||
while (bytesWritten < totalBytes && retryCount < MAX_RETRIES) {
|
||||
auto beginTime = std::chrono::steady_clock::now();
|
||||
while (bytesWritten < totalBytes) {
|
||||
ssize_t writeResult = write(fd, data + bytesWritten, totalBytes - bytesWritten);
|
||||
if (writeResult == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY_MS));
|
||||
retryCount++;
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteTask failed: write error=%{public}s for sessionId=%{public}s",
|
||||
strerror(errno), sessionId.c_str());
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
} else if (writeResult == 0) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteMessage failed: write error=%{public}s for sessionId=%{public}s",
|
||||
strerror(errno), sessionId.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - beginTime).count();
|
||||
if (elapsed >= INPUT_WRITE_TIMEOUT_MS) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteMessage failed: wait writable timeout for sessionId=%{public}s, "
|
||||
"wrote=%{public}zu/%{public}zu",
|
||||
sessionId.c_str(), bytesWritten, totalBytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
pollfd pollFd {};
|
||||
pollFd.fd = fd;
|
||||
pollFd.events = POLLOUT;
|
||||
int32_t pollTimeout = std::min<int64_t>(INPUT_WRITE_POLL_MS, INPUT_WRITE_TIMEOUT_MS - elapsed);
|
||||
int32_t pollResult = poll(&pollFd, 1, pollTimeout);
|
||||
if (pollResult < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteMessage failed: poll error=%{public}s for sessionId=%{public}s",
|
||||
strerror(errno), sessionId.c_str());
|
||||
return false;
|
||||
}
|
||||
if (pollResult == 0) {
|
||||
continue;
|
||||
}
|
||||
if ((pollFd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteMessage failed: poll revents=%{public}d for sessionId=%{public}s",
|
||||
pollFd.revents, sessionId.c_str());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (writeResult == 0) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL,
|
||||
"WriteTask failed: pipe closed for sessionId=%{public}s",
|
||||
"WriteMessage failed: pipe closed for sessionId=%{public}s",
|
||||
sessionId.c_str());
|
||||
result = false;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesWritten += writeResult;
|
||||
retryCount = 0;
|
||||
}
|
||||
if (bytesWritten < totalBytes) {
|
||||
result = false;
|
||||
}
|
||||
if (result == false) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "WriteTask: partial write for sessionId=%{public}s, "
|
||||
"wrote=%{public}zu/%{public}zu", sessionId.c_str(), bytesWritten, totalBytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IOMonitor::NotifyInputReply(const std::string &sessionId, const std::string &eventId, bool result)
|
||||
{
|
||||
if (inputReplyCallback_) {
|
||||
inputReplyCallback_(sessionId, eventId, result);
|
||||
}
|
||||
@@ -241,15 +305,97 @@ void IOMonitor::WriteTask(const std::string &sessionId, const std::string &messa
|
||||
|
||||
void IOMonitor::SendMessage(const std::string &sessionId, const std::string &message, const std::string &eventId)
|
||||
{
|
||||
auto writeTask = [weak = weak_from_this(), sessionId, message, eventId]() {
|
||||
bool shouldSubmit = false;
|
||||
bool rejected = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
if (GetStdinFdLocked(sessionId) < 0) {
|
||||
rejected = true;
|
||||
} else {
|
||||
auto &queue = inputQueues_[sessionId];
|
||||
if (queue.pendingBytes + message.size() > MAX_PENDING_INPUT_BYTES ||
|
||||
queue.pendingInputs.size() >= MAX_PENDING_INPUT_MESSAGES) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL,
|
||||
"SendMessage failed: input queue full for sessionId=%{public}s, pendingBytes=%{public}zu, "
|
||||
"pendingMessages=%{public}zu",
|
||||
sessionId.c_str(), queue.pendingBytes, queue.pendingInputs.size());
|
||||
rejected = true;
|
||||
} else {
|
||||
queue.pendingInputs.emplace_back(PendingInput {message, eventId});
|
||||
queue.pendingBytes += message.size();
|
||||
if (!queue.writeTaskRunning) {
|
||||
queue.writeTaskRunning = true;
|
||||
shouldSubmit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rejected) {
|
||||
NotifyInputReply(sessionId, eventId, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldSubmit) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto writeTask = [weak = weak_from_this(), sessionId]() {
|
||||
auto sharedThis = weak.lock();
|
||||
if (sharedThis) {
|
||||
sharedThis->WriteTask(sessionId, message, eventId);
|
||||
sharedThis->ProcessWriteQueue(sessionId);
|
||||
}
|
||||
};
|
||||
ffrt::submit(std::move(writeTask));
|
||||
}
|
||||
|
||||
void IOMonitor::ProcessWriteQueue(const std::string &sessionId)
|
||||
{
|
||||
while (true) {
|
||||
PendingInput input;
|
||||
int fd = -1;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
auto queueIt = inputQueues_.find(sessionId);
|
||||
if (queueIt == inputQueues_.end() || queueIt->second.pendingInputs.empty()) {
|
||||
if (queueIt != inputQueues_.end()) {
|
||||
queueIt->second.writeTaskRunning = false;
|
||||
if (queueIt->second.pendingBytes == 0) {
|
||||
inputQueues_.erase(queueIt);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
input = std::move(queueIt->second.pendingInputs.front());
|
||||
queueIt->second.pendingInputs.pop_front();
|
||||
queueIt->second.pendingBytes -= input.message.size();
|
||||
fd = GetStdinFdLocked(sessionId);
|
||||
}
|
||||
|
||||
bool result = fd >= 0 && WriteMessage(fd, sessionId, input.message);
|
||||
NotifyInputReply(sessionId, input.eventId, result);
|
||||
if (!result) {
|
||||
std::vector<PendingInput> failedInputs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fdMutex_);
|
||||
auto queueIt = inputQueues_.find(sessionId);
|
||||
if (queueIt != inputQueues_.end()) {
|
||||
while (!queueIt->second.pendingInputs.empty()) {
|
||||
failedInputs.emplace_back(std::move(queueIt->second.pendingInputs.front()));
|
||||
queueIt->second.pendingInputs.pop_front();
|
||||
}
|
||||
inputQueues_.erase(queueIt);
|
||||
}
|
||||
}
|
||||
for (const auto &failedInput : failedInputs) {
|
||||
NotifyInputReply(sessionId, failedInput.eventId, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IOMonitor::MonitorLoop()
|
||||
{
|
||||
epoll_event events[MAX_EVENTS];
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "permission_query_util.h"
|
||||
#include "cli_tool_data_manager.h"
|
||||
#include "hilog_tag_wrapper.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t PermissionQueryUtil::BatchQueryPermissions(
|
||||
const std::vector<Command> &cmds,
|
||||
std::vector<CommandPermission> &cmdPermissions)
|
||||
{
|
||||
cmdPermissions.clear();
|
||||
cmdPermissions.reserve(cmds.size());
|
||||
for (const auto &cmd : cmds) {
|
||||
std::vector<std::string> permissions;
|
||||
int32_t ret = QuerySingleCommand(cmd, permissions);
|
||||
int32_t queryRet;
|
||||
if (ret == ERR_OK) {
|
||||
queryRet = QueryResult::SUCCESS;
|
||||
} else if (ret == ERR_TOOL_NOT_EXIST) {
|
||||
queryRet = QueryResult::COMMAND_NOT_EXIST;
|
||||
permissions.clear();
|
||||
} else {
|
||||
queryRet = QueryResult::DB_ERROR;
|
||||
permissions.clear();
|
||||
}
|
||||
cmdPermissions.push_back(BuildCommandPermission(cmd, permissions, queryRet));
|
||||
}
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Batch query completed, total=%{public}zu", cmdPermissions.size());
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t PermissionQueryUtil::QuerySingleCommand(
|
||||
const Command &cmd,
|
||||
std::vector<std::string> &permissions)
|
||||
{
|
||||
if (cmd.toolName.empty()) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "Tool name is empty");
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
if (cmd.subCommand.empty()) {
|
||||
return QueryMainCommandPermission(cmd.toolName, permissions);
|
||||
}
|
||||
return QuerySubCommandPermission(cmd.toolName, cmd.subCommand, permissions);
|
||||
}
|
||||
|
||||
int32_t PermissionQueryUtil::QueryMainCommandPermission(
|
||||
const std::string &toolName,
|
||||
std::vector<std::string> &permissions)
|
||||
{
|
||||
ToolInfo toolInfo;
|
||||
int32_t ret = CliToolDataManager::GetInstance().GetToolByName(toolName, toolInfo);
|
||||
if (ret == ERR_NO_INIT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "DB error when querying tool: %{public}s", toolName.c_str());
|
||||
return ERR_NO_INIT;
|
||||
} else if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool not found: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
permissions = toolInfo.requirePermissions;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t PermissionQueryUtil::QuerySubCommandPermission(
|
||||
const std::string &toolName,
|
||||
const std::string &subCommand,
|
||||
std::vector<std::string> &permissions)
|
||||
{
|
||||
ToolInfo toolInfo;
|
||||
int32_t ret = CliToolDataManager::GetInstance().GetToolByName(toolName, toolInfo);
|
||||
if (ret == ERR_NO_INIT) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "DB error when querying tool: %{public}s", toolName.c_str());
|
||||
return ERR_NO_INIT;
|
||||
} else if (ret != ERR_OK) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool not found: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
if (!toolInfo.hasSubCommand) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Tool has no subcommand: %{public}s", toolName.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
auto it = toolInfo.subcommands.find(subCommand);
|
||||
if (it == toolInfo.subcommands.end()) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "Subcommand not found: %{public}s.%{public}s",
|
||||
toolName.c_str(), subCommand.c_str());
|
||||
return ERR_TOOL_NOT_EXIST;
|
||||
}
|
||||
permissions = it->second.requirePermissions;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
CommandPermission PermissionQueryUtil::BuildCommandPermission(
|
||||
const Command &cmd,
|
||||
const std::vector<std::string> &permissions,
|
||||
int32_t queryRet)
|
||||
{
|
||||
CommandPermission cmdPerm;
|
||||
cmdPerm.cmd = cmd;
|
||||
cmdPerm.permissions = permissions;
|
||||
cmdPerm.queryRet = queryRet;
|
||||
return cmdPerm;
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -128,7 +128,8 @@ int32_t ProcessManager::CreateChildProcess(const ExecToolParam ¶m, const std
|
||||
execArgs.push_back(nullptr);
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "Before execvp");
|
||||
execvp(execArgs[0], execArgs.data());
|
||||
_exit(0);
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "execvp failed:%{public}d", errno);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Parent process: close write ends of pipes
|
||||
@@ -141,11 +142,11 @@ int32_t ProcessManager::CreateChildProcess(const ExecToolParam ¶m, const std
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
bool ProcessManager::TerminateProcess(pid_t pid, int signal) const
|
||||
bool ProcessManager::Killpg(pid_t pid) const
|
||||
{
|
||||
if (pid > 0 && kill(pid, signal) != 0 && errno != ESRCH) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "Failed to kill process %{public}d: %{public}s",
|
||||
pid, strerror(errno));
|
||||
int32_t killRet = kill(0 - pid, SIGTERM);
|
||||
if (killRet != 0) {
|
||||
TAG_LOGW(AAFwkTag::CLI_TOOL, "killpg result:%{public}d", killRet);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -28,7 +28,7 @@ SessionState SessionRecord::GetState() const
|
||||
return state_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void SessionRecord::SetTerminalResult(int32_t status, int32_t sig)
|
||||
void SessionRecord::SetTerminalResult(int32_t status, int32_t sig) // for waitpid
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resultMutex_);
|
||||
terminalStatus_ = status;
|
||||
@@ -38,6 +38,18 @@ void SessionRecord::SetTerminalResult(int32_t status, int32_t sig)
|
||||
processExited_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SessionRecord::SetSkillResult(int32_t resultCode, const std::string &outputText)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resultMutex_);
|
||||
terminalStatus_ = resultCode;
|
||||
stdoutText_ = outputText;
|
||||
endTimeMs_ = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
processExited_.store(true, std::memory_order_release);
|
||||
stdoutClosed_.store(true, std::memory_order_release);
|
||||
stderrClosed_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
int32_t SessionRecord::GetTerminalStatus() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resultMutex_);
|
||||
@@ -120,14 +132,12 @@ void SessionRecord::BuildSessionInfo(CliSessionInfo &session) const
|
||||
session.sessionId = sessionId;
|
||||
session.toolName = toolName;
|
||||
|
||||
if (!HasProcessExited() || !OutputDrained()) {
|
||||
session.result = nullptr;
|
||||
if ((!HasProcessExited() || !OutputDrained()) && !timedOut_) {
|
||||
session.status = "running";
|
||||
} else {
|
||||
session.result = BuildExecResult();
|
||||
session.status =
|
||||
(!session.result || session.result->timedOut || session.result->exitCode != 0) ?
|
||||
"failed" : "completed";
|
||||
(!session.result || session.result->timedOut || session.result->exitCode != 0) ? "failed" : "completed";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,17 +152,17 @@ void SessionRecord::TrimBufferedOutput(std::string &buffer)
|
||||
std::shared_ptr<ExecResult> SessionRecord::BuildExecResult() const
|
||||
{
|
||||
auto result = std::make_shared<ExecResult>();
|
||||
if (result == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(resultMutex_);
|
||||
result->exitCode = terminalStatus_;
|
||||
if (timedOut_) {
|
||||
result->executionTime = timeoutMs;
|
||||
} else {
|
||||
result->exitCode = terminalStatus_;
|
||||
result->executionTime = (endTimeMs_ > startTime) ? (endTimeMs_ - startTime) : 0;
|
||||
}
|
||||
result->outputText = stdoutText_;
|
||||
result->errorText = stderrText_;
|
||||
result->signalNumber = signalNumber_;
|
||||
result->timedOut = timedOut_;
|
||||
result->executionTime = (endTimeMs_ > startTime) ? (endTimeMs_ - startTime) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,16 +26,18 @@ bool PermissionUtil::VerifyAccessToken(AccessToken::AccessTokenID tokenId,
|
||||
std::vector<int32_t> perStateList;
|
||||
auto permRet = AccessToken::AccessTokenKit::VerifyAccessToken(tokenId, requirePermissions,
|
||||
perStateList);
|
||||
if (permRet != AccessToken::PermissionState::PERMISSION_GRANTED) {
|
||||
if (permRet == 0) {
|
||||
for (size_t index = 0; index < perStateList.size(); index++) {
|
||||
if (perStateList[index] == AccessToken::TypePermissionState::PERMISSION_DENIED) {
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "%{public}d not has %{public}s", tokenId,
|
||||
requirePermissions[index].c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
TAG_LOGE(AAFwkTag::CLI_TOOL, "fail to call VerifyAccessToken");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PermissionUtil::VerifyAccessToken(AccessToken::AccessTokenID tokenId, const std::string &requirePermission)
|
||||
|
||||
@@ -17,14 +17,26 @@ import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"arg_mapping_test:arg_mapping_test",
|
||||
"cli_tool_mgr_client_test:cli_tool_mgr_client_test",
|
||||
"cli_tool_mgr_service_test:cli_tool_mgr_service_test",
|
||||
"cli_common_util_test:cli_common_util_test",
|
||||
"cli_event_reply_manager_test:cli_event_reply_manager_test",
|
||||
"cli_tool_data_manager_test:cli_tool_data_manager_test",
|
||||
"cli_tool_event_test:cli_tool_event_test",
|
||||
"cli_tool_mgr_client_test:cli_tool_mgr_client_test",
|
||||
"cli_tool_mgr_scheduler_recipient_test:cli_tool_mgr_scheduler_recipient_test",
|
||||
"cli_tool_mgr_service_test:cli_tool_mgr_service_test",
|
||||
"cli_session_info_test:cli_session_info_test",
|
||||
"cli_session_subscription_manager_test:cli_session_subscription_manager_test",
|
||||
"event_dispatcher_test:event_dispatcher_test",
|
||||
"exec_options_test:exec_options_test",
|
||||
"exec_result_test:exec_result_test",
|
||||
"exec_tool_param_test:exec_tool_param_test",
|
||||
"io_monitor_test:io_monitor_test",
|
||||
"permission_query_util_test:permission_query_util_test",
|
||||
"process_manager_test:process_manager_test",
|
||||
"session_record_test:session_record_test",
|
||||
"sub_command_info_test:sub_command_info_test",
|
||||
"tool_summary_test:tool_summary_test",
|
||||
"tool_info_test:tool_info_test",
|
||||
"tool_summary_test:tool_summary_test",
|
||||
"tool_util_test:tool_util_test",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_common_util_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"mock/include",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
"${cli_tool_framework_path}/services/common/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_common_util_test.cpp",
|
||||
"mock/src/cli_common_mock.cpp",
|
||||
"${cli_tool_framework_path}/services/common/src/ccm_util.cpp",
|
||||
"${cli_tool_framework_path}/services/common/src/permission_util.cpp",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"access_token:libaccesstoken_sdk",
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_common_util_test" ]
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
|
||||
#define private public
|
||||
#include "ccm_util.h"
|
||||
#undef private
|
||||
#include "cli_common_mock.h"
|
||||
#include "permission_util.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t CUSTOM_CLI_LIMIT = 16;
|
||||
constexpr Security::AccessToken::AccessTokenID TEST_TOKEN_ID = 100;
|
||||
}
|
||||
|
||||
class CliCommonUtilTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
CliCommonMock::Reset();
|
||||
auto &ccmUtil = CcmUtil::GetInstance();
|
||||
ccmUtil.maxCliQuantity_.isLoaded = false;
|
||||
ccmUtil.maxCliQuantity_.value = DEFAULT_MAX_CLI_QUANTITY;
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
CliCommonMock::Reset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @tc.name: CcmUtil_GetCliConcurrencyLimit_0100
|
||||
* @tc.desc: Test ccm util loads parameter once and then uses cached value
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliCommonUtilTest, CcmUtil_GetCliConcurrencyLimit_0100, TestSize.Level1)
|
||||
{
|
||||
CliCommonMock::intParameterValue = CUSTOM_CLI_LIMIT;
|
||||
EXPECT_EQ(CcmUtil::GetInstance().GetCliConcurrencyLimit(), CUSTOM_CLI_LIMIT);
|
||||
|
||||
CliCommonMock::intParameterValue = CUSTOM_CLI_LIMIT + 1;
|
||||
EXPECT_EQ(CcmUtil::GetInstance().GetCliConcurrencyLimit(), CUSTOM_CLI_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: PermissionUtil_VerifyAccessToken_0100
|
||||
* @tc.desc: Test vector permission grant and denial branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliCommonUtilTest, PermissionUtil_VerifyAccessToken_0100, TestSize.Level1)
|
||||
{
|
||||
std::vector<std::string> permissions = {
|
||||
"ohos.permission.EXEC_CLI_TOOL",
|
||||
"ohos.permission.QUERY_CLI_TOOL",
|
||||
};
|
||||
|
||||
// Test all permissions granted - permRet should be 0 for success
|
||||
CliCommonMock::vectorPermissionResult = 0;
|
||||
CliCommonMock::permissionStateList = {
|
||||
Security::AccessToken::TypePermissionState::PERMISSION_GRANTED,
|
||||
Security::AccessToken::TypePermissionState::PERMISSION_GRANTED,
|
||||
};
|
||||
EXPECT_TRUE(PermissionUtil::VerifyAccessToken(TEST_TOKEN_ID, permissions));
|
||||
|
||||
// Test first permission denied - should return false immediately
|
||||
CliCommonMock::vectorPermissionResult = 0;
|
||||
CliCommonMock::permissionStateList = {
|
||||
Security::AccessToken::TypePermissionState::PERMISSION_DENIED,
|
||||
Security::AccessToken::TypePermissionState::PERMISSION_GRANTED,
|
||||
};
|
||||
EXPECT_FALSE(PermissionUtil::VerifyAccessToken(TEST_TOKEN_ID, permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: PermissionUtil_VerifyAccessToken_0200
|
||||
* @tc.desc: Test single permission grant and denial branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliCommonUtilTest, PermissionUtil_VerifyAccessToken_0200, TestSize.Level1)
|
||||
{
|
||||
CliCommonMock::singlePermissionResult = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
|
||||
EXPECT_TRUE(PermissionUtil::VerifyAccessToken(TEST_TOKEN_ID, "ohos.permission.EXEC_CLI_TOOL"));
|
||||
|
||||
CliCommonMock::singlePermissionResult = Security::AccessToken::PermissionState::PERMISSION_DENIED;
|
||||
EXPECT_FALSE(PermissionUtil::VerifyAccessToken(TEST_TOKEN_ID, "ohos.permission.EXEC_CLI_TOOL"));
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_ACCESSTOKEN_KIT_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_ACCESSTOKEN_KIT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "access_token.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace Security {
|
||||
namespace AccessToken {
|
||||
class AccessTokenKit {
|
||||
public:
|
||||
static int32_t VerifyAccessToken(AccessTokenID tokenId, const std::vector<std::string> &permissions,
|
||||
std::vector<int32_t> &permStateList);
|
||||
static int32_t VerifyAccessToken(AccessTokenID tokenId, const std::string &permissionName, bool crossUser);
|
||||
};
|
||||
} // namespace AccessToken
|
||||
} // namespace Security
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_ACCESSTOKEN_KIT_H
|
||||
+18
-14
@@ -13,21 +13,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "exec_tool_callback_impl.h"
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_H
|
||||
|
||||
#include "hilog_tag_wrapper.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t ExecToolCallbackImpl::SendResult(const CliSessionInfo &session)
|
||||
{
|
||||
TAG_LOGI(AAFwkTag::CLI_TOOL, "ExecToolCallbackImpl send result, sessionId=%{public}s, status=%{public}s",
|
||||
session.sessionId.c_str(), session.status.c_str());
|
||||
if (task_) {
|
||||
TAG_LOGD(AAFwkTag::CLI_TOOL, "ExecToolCallbackImpl invoke callback");
|
||||
task_(session);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
class CliCommonMock {
|
||||
public:
|
||||
static int32_t intParameterValue;
|
||||
static int32_t vectorPermissionResult;
|
||||
static int32_t singlePermissionResult;
|
||||
static std::vector<int32_t> permissionStateList;
|
||||
|
||||
static void Reset();
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_H
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_PARAMETERS_H
|
||||
#define OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_PARAMETERS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cli_common_mock.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace system {
|
||||
template<typename T>
|
||||
T GetIntParameter(const std::string &, T)
|
||||
{
|
||||
return static_cast<T>(CliTool::CliCommonMock::intParameterValue);
|
||||
}
|
||||
} // namespace system
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_PARAMETERS_H
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 "cli_common_mock.h"
|
||||
|
||||
#include "accesstoken_kit.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t CliCommonMock::intParameterValue = 8;
|
||||
int32_t CliCommonMock::vectorPermissionResult = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
|
||||
int32_t CliCommonMock::singlePermissionResult = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
|
||||
std::vector<int32_t> CliCommonMock::permissionStateList;
|
||||
|
||||
void CliCommonMock::Reset()
|
||||
{
|
||||
intParameterValue = 8;
|
||||
vectorPermissionResult = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
|
||||
singlePermissionResult = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
|
||||
permissionStateList.clear();
|
||||
}
|
||||
} // namespace CliTool
|
||||
|
||||
namespace Security {
|
||||
namespace AccessToken {
|
||||
int32_t AccessTokenKit::VerifyAccessToken(AccessTokenID, const std::vector<std::string> &permissions,
|
||||
std::vector<int32_t> &permStateList)
|
||||
{
|
||||
if (!CliTool::CliCommonMock::permissionStateList.empty()) {
|
||||
permStateList = CliTool::CliCommonMock::permissionStateList;
|
||||
} else {
|
||||
permStateList.assign(permissions.size(), CliTool::CliCommonMock::vectorPermissionResult);
|
||||
}
|
||||
return CliTool::CliCommonMock::vectorPermissionResult;
|
||||
}
|
||||
|
||||
int32_t AccessTokenKit::VerifyAccessToken(AccessTokenID, const std::string &, bool)
|
||||
{
|
||||
return CliTool::CliCommonMock::singlePermissionResult;
|
||||
}
|
||||
} // namespace AccessToken
|
||||
} // namespace Security
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_event_reply_manager_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${cli_tool_framework_path}/test/unittest/cli_tool_mgr_client_test/mock/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_event_reply_manager_test.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_event_reply_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_result.cpp",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"ability_base:want",
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_core",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_event_reply_manager_test" ]
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "cli_event_reply_manager.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t ERR_OK = 0;
|
||||
constexpr int32_t ERROR_CODE = -1;
|
||||
constexpr int32_t TEST_RESULT_CODE = 1001;
|
||||
}
|
||||
|
||||
class CliEventReplyManagerTest : public testing::Test {
|
||||
public:
|
||||
void TearDown() override
|
||||
{
|
||||
CliEventReplyManager::GetInstance().ClearAllEvent();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @tc.name: CliEventReplyManager_0100
|
||||
* @tc.desc: Test reply manager active, deferred, missing, null callback and remove branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliEventReplyManagerTest, CliEventReplyManager_0100, TestSize.Level1)
|
||||
{
|
||||
auto &manager = CliEventReplyManager::GetInstance();
|
||||
int32_t callbackCount = 0;
|
||||
int32_t callbackCode = 0;
|
||||
std::string activeEventId = manager.AddEventReplyCallback("active-", [&](const CliEventReplyResult &result) {
|
||||
callbackCount++;
|
||||
callbackCode = result.code;
|
||||
});
|
||||
|
||||
manager.ActivateEventReplyCallback(activeEventId);
|
||||
EXPECT_EQ(manager.HandleEventReply(activeEventId, CliEventReplyResult {TEST_RESULT_CODE, std::nullopt}), ERR_OK);
|
||||
EXPECT_EQ(callbackCount, 1);
|
||||
EXPECT_EQ(callbackCode, TEST_RESULT_CODE);
|
||||
EXPECT_EQ(manager.HandleEventReply(activeEventId, CliEventReplyResult {TEST_RESULT_CODE, std::nullopt}),
|
||||
ERROR_CODE);
|
||||
|
||||
std::optional<CliSessionInfo> deferredSession;
|
||||
std::string deferredEventId = manager.AddEventReplyCallback("deferred-", [&](const CliEventReplyResult &result) {
|
||||
callbackCount++;
|
||||
deferredSession = result.sessionInfo;
|
||||
});
|
||||
CliSessionInfo session;
|
||||
session.sessionId = "session";
|
||||
session.toolName = "tool";
|
||||
session.status = "running";
|
||||
EXPECT_EQ(manager.HandleEventReply(deferredEventId, CliEventReplyResult {ERR_OK, session}), ERR_OK);
|
||||
EXPECT_EQ(callbackCount, 1);
|
||||
manager.ActivateEventReplyCallback(deferredEventId);
|
||||
EXPECT_EQ(callbackCount, 2);
|
||||
ASSERT_TRUE(deferredSession.has_value());
|
||||
EXPECT_EQ(deferredSession->sessionId, "session");
|
||||
|
||||
std::string nullEventId = manager.AddEventReplyCallback("null-", nullptr);
|
||||
manager.ActivateEventReplyCallback(nullEventId);
|
||||
EXPECT_EQ(manager.HandleEventReply(nullEventId, CliEventReplyResult {ERR_OK, std::nullopt}), ERROR_CODE);
|
||||
|
||||
std::string removedEventId = manager.AddEventReplyCallback("removed-", [&](const CliEventReplyResult &) {});
|
||||
manager.RemoveEventReplyCallback(removedEventId);
|
||||
EXPECT_EQ(manager.HandleEventReply(removedEventId, CliEventReplyResult {ERR_OK, std::nullopt}), ERROR_CODE);
|
||||
|
||||
manager.ActivateEventReplyCallback("missing-event");
|
||||
std::string inactiveEventId = manager.AddEventReplyCallback("inactive-", [&](const CliEventReplyResult &) {
|
||||
callbackCount++;
|
||||
});
|
||||
manager.ActivateEventReplyCallback(inactiveEventId);
|
||||
EXPECT_EQ(callbackCount, 2);
|
||||
EXPECT_EQ(manager.HandleEventReply(inactiveEventId, CliEventReplyResult {ERR_OK, std::nullopt}), ERR_OK);
|
||||
EXPECT_EQ(callbackCount, 3);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_session_info_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_session_info_test.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_result.cpp",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_core",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_session_info_test" ]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <parcel.h>
|
||||
|
||||
#include "cli_session_info.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t TEST_EXIT_CODE = 7;
|
||||
}
|
||||
|
||||
class CliSessionInfoTest : public testing::Test {};
|
||||
|
||||
/**
|
||||
* @tc.name: CliSessionInfo_Parcelable_0100
|
||||
* @tc.desc: Test CliSessionInfo optional result marshalling branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliSessionInfoTest, CliSessionInfo_Parcelable_0100, TestSize.Level1)
|
||||
{
|
||||
CliSessionInfo runningInfo;
|
||||
runningInfo.sessionId = "session-running";
|
||||
runningInfo.toolName = "tool";
|
||||
runningInfo.status = "running";
|
||||
|
||||
Parcel runningParcel;
|
||||
ASSERT_TRUE(runningInfo.Marshalling(runningParcel));
|
||||
runningParcel.RewindRead(0);
|
||||
std::unique_ptr<CliSessionInfo> runningResult(CliSessionInfo::Unmarshalling(runningParcel));
|
||||
ASSERT_NE(runningResult, nullptr);
|
||||
EXPECT_EQ(runningResult->sessionId, "session-running");
|
||||
EXPECT_EQ(runningResult->toolName, "tool");
|
||||
EXPECT_EQ(runningResult->status, "running");
|
||||
EXPECT_EQ(runningResult->result, nullptr);
|
||||
|
||||
CliSessionInfo completedInfo;
|
||||
completedInfo.sessionId = "session-completed";
|
||||
completedInfo.toolName = "tool";
|
||||
completedInfo.status = "completed";
|
||||
completedInfo.result = std::make_shared<ExecResult>();
|
||||
completedInfo.result->exitCode = TEST_EXIT_CODE;
|
||||
completedInfo.result->outputText = "ok";
|
||||
|
||||
Parcel completedParcel;
|
||||
ASSERT_TRUE(completedInfo.Marshalling(completedParcel));
|
||||
completedParcel.RewindRead(0);
|
||||
std::unique_ptr<CliSessionInfo> completedResult(CliSessionInfo::Unmarshalling(completedParcel));
|
||||
ASSERT_NE(completedResult, nullptr);
|
||||
ASSERT_NE(completedResult->result, nullptr);
|
||||
EXPECT_EQ(completedResult->status, "completed");
|
||||
EXPECT_EQ(completedResult->result->exitCode, TEST_EXIT_CODE);
|
||||
EXPECT_EQ(completedResult->result->outputText, "ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliSessionInfo_Unmarshalling_0200
|
||||
* @tc.desc: Test CliSessionInfo unmarshalling failure branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliSessionInfoTest, CliSessionInfo_Unmarshalling_0200, TestSize.Level1)
|
||||
{
|
||||
Parcel emptyParcel;
|
||||
EXPECT_EQ(CliSessionInfo::Unmarshalling(emptyParcel), nullptr);
|
||||
|
||||
Parcel missingStatusParcel;
|
||||
ASSERT_TRUE(missingStatusParcel.WriteString("session"));
|
||||
ASSERT_TRUE(missingStatusParcel.WriteString("tool"));
|
||||
missingStatusParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliSessionInfo::Unmarshalling(missingStatusParcel), nullptr);
|
||||
|
||||
Parcel missingToolNameParcel;
|
||||
ASSERT_TRUE(missingToolNameParcel.WriteString("session"));
|
||||
missingToolNameParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliSessionInfo::Unmarshalling(missingToolNameParcel), nullptr);
|
||||
|
||||
Parcel missingHasResultParcel;
|
||||
ASSERT_TRUE(missingHasResultParcel.WriteString("session"));
|
||||
ASSERT_TRUE(missingHasResultParcel.WriteString("tool"));
|
||||
ASSERT_TRUE(missingHasResultParcel.WriteString("running"));
|
||||
missingHasResultParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliSessionInfo::Unmarshalling(missingHasResultParcel), nullptr);
|
||||
|
||||
Parcel missingResultParcel;
|
||||
ASSERT_TRUE(missingResultParcel.WriteString("session"));
|
||||
ASSERT_TRUE(missingResultParcel.WriteString("tool"));
|
||||
ASSERT_TRUE(missingResultParcel.WriteString("completed"));
|
||||
ASSERT_TRUE(missingResultParcel.WriteBool(true));
|
||||
missingResultParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliSessionInfo::Unmarshalling(missingResultParcel), nullptr);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_session_subscription_manager_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_session_subscription_manager_test.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_subscription_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_event.cpp",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_core",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_session_subscription_manager_test" ]
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cli_session_subscription_manager.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t ERR_OK = 0;
|
||||
constexpr int32_t ERROR_CODE = -1;
|
||||
constexpr int32_t TEST_EXIT_CODE = 7;
|
||||
}
|
||||
|
||||
class CliSessionSubscriptionManagerTest : public testing::Test {
|
||||
public:
|
||||
void TearDown() override
|
||||
{
|
||||
CliSessionSubscriptionManager::GetInstance().ClearAllSubscriptions();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @tc.name: CliSessionSubscriptionManager_0100
|
||||
* @tc.desc: Test subscription manager active, deferred, exit, invalid and remove branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliSessionSubscriptionManagerTest, CliSessionSubscriptionManager_0100, TestSize.Level1)
|
||||
{
|
||||
auto &manager = CliSessionSubscriptionManager::GetInstance();
|
||||
CliToolEvent stdoutEvent;
|
||||
stdoutEvent.type = "stdout";
|
||||
stdoutEvent.eventData = "hello";
|
||||
CliToolEvent exitEvent;
|
||||
exitEvent.type = "exit";
|
||||
exitEvent.exitCode = TEST_EXIT_CODE;
|
||||
|
||||
int32_t callbackCount = 0;
|
||||
std::vector<std::string> eventTypes;
|
||||
std::string subscriptionId;
|
||||
subscriptionId = manager.AddProvisionalSubscription("session", [&](const std::string &sessionId,
|
||||
const std::string &callbackSubscriptionId, const CliToolEvent &event) {
|
||||
EXPECT_EQ(sessionId, "session");
|
||||
EXPECT_EQ(callbackSubscriptionId, subscriptionId);
|
||||
callbackCount++;
|
||||
eventTypes.push_back(event.type);
|
||||
});
|
||||
ASSERT_FALSE(subscriptionId.empty());
|
||||
EXPECT_EQ(manager.HandleSessionEvent("session", subscriptionId, stdoutEvent), ERR_OK);
|
||||
EXPECT_EQ(callbackCount, 0);
|
||||
manager.ActivateSubscription(subscriptionId);
|
||||
EXPECT_EQ(callbackCount, 1);
|
||||
EXPECT_EQ(eventTypes.back(), "stdout");
|
||||
EXPECT_EQ(manager.HandleSessionEvent("session", subscriptionId, exitEvent), ERR_OK);
|
||||
EXPECT_EQ(callbackCount, 2);
|
||||
EXPECT_EQ(eventTypes.back(), "exit");
|
||||
EXPECT_EQ(manager.HandleSessionEvent("session", subscriptionId, stdoutEvent), ERROR_CODE);
|
||||
|
||||
EXPECT_TRUE(manager.AddProvisionalSubscription("", [&](const std::string &, const std::string &,
|
||||
const CliToolEvent &) {}).empty());
|
||||
EXPECT_TRUE(manager.AddProvisionalSubscription("session", nullptr).empty());
|
||||
EXPECT_EQ(manager.HandleSessionEvent("bad-session", "bad-subscription", stdoutEvent), ERROR_CODE);
|
||||
|
||||
int32_t pendingExitCount = 0;
|
||||
std::string pendingExitId = manager.AddProvisionalSubscription("exit-session", [&](const std::string &,
|
||||
const std::string &, const CliToolEvent &) {
|
||||
pendingExitCount++;
|
||||
});
|
||||
ASSERT_FALSE(pendingExitId.empty());
|
||||
EXPECT_EQ(manager.HandleSessionEvent("exit-session", pendingExitId, stdoutEvent), ERR_OK);
|
||||
EXPECT_EQ(manager.HandleSessionEvent("exit-session", pendingExitId, exitEvent), ERR_OK);
|
||||
manager.ActivateSubscription(pendingExitId);
|
||||
EXPECT_EQ(pendingExitCount, 2);
|
||||
EXPECT_EQ(manager.HandleSessionEvent("exit-session", pendingExitId, stdoutEvent), ERROR_CODE);
|
||||
|
||||
std::string removedId = manager.AddProvisionalSubscription("remove-session", [&](const std::string &,
|
||||
const std::string &, const CliToolEvent &) {});
|
||||
ASSERT_FALSE(removedId.empty());
|
||||
manager.RemoveSubscription(removedId);
|
||||
manager.ActivateSubscription(removedId);
|
||||
EXPECT_EQ(manager.HandleSessionEvent("remove-session", removedId, stdoutEvent), ERROR_CODE);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
+1238
File diff suppressed because it is too large
Load Diff
+25
@@ -45,12 +45,32 @@ public:
|
||||
DistributedKv::Status GetEntries(
|
||||
const DistributedKv::Key &prefix, std::vector<DistributedKv::Entry> &entries) const override
|
||||
{
|
||||
if (GetEntries_ != DistributedKv::Status::SUCCESS) {
|
||||
return GetEntries_;
|
||||
}
|
||||
entries.clear();
|
||||
for (const auto &item : mockData_) {
|
||||
DistributedKv::Entry entry;
|
||||
entry.key = DistributedKv::Key(item.first);
|
||||
entry.value = item.second;
|
||||
entries.push_back(entry);
|
||||
}
|
||||
return GetEntries_;
|
||||
};
|
||||
|
||||
DistributedKv::Status GetEntries(
|
||||
const DistributedKv::DataQuery &query, std::vector<DistributedKv::Entry> &entries) const override
|
||||
{
|
||||
if (GetEntries_ != DistributedKv::Status::SUCCESS) {
|
||||
return GetEntries_;
|
||||
}
|
||||
entries.clear();
|
||||
for (const auto &item : mockData_) {
|
||||
DistributedKv::Entry entry;
|
||||
entry.key = DistributedKv::Key(item.first);
|
||||
entry.value = item.second;
|
||||
entries.push_back(entry);
|
||||
}
|
||||
return GetEntries_;
|
||||
};
|
||||
|
||||
@@ -231,6 +251,11 @@ public:
|
||||
mockData_[key] = DistributedKv::Value(value);
|
||||
}
|
||||
|
||||
bool HasMockData(const std::string &key) const
|
||||
{
|
||||
return mockData_.find(key) != mockData_.end();
|
||||
}
|
||||
|
||||
DistributedKv::Status GetEntries_ = DistributedKv::Status::SUCCESS;
|
||||
DistributedKv::Status Delete_ = DistributedKv::Status::SUCCESS;
|
||||
DistributedKv::Status Put_ = DistributedKv::Status::SUCCESS;
|
||||
+8
-14
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"),
|
||||
# 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
|
||||
#
|
||||
@@ -16,31 +16,25 @@ import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("arg_mapping_test") {
|
||||
ohos_unittest("cli_tool_event_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [ "${cli_tool_framework_path}/interfaces/cli_tool/include" ]
|
||||
|
||||
sources = [ "arg_mapping_test.cpp" ]
|
||||
|
||||
cflags = []
|
||||
if (target_cpu == "arm") {
|
||||
cflags += [ "-BINDER_IPC_32BIT" ]
|
||||
}
|
||||
|
||||
deps = [ "${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client" ]
|
||||
sources = [
|
||||
"cli_tool_event_test.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_event.cpp",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_core",
|
||||
"json:nlohmann_json_static",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":arg_mapping_test" ]
|
||||
}
|
||||
deps = [ ":cli_tool_event_test" ]
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <parcel.h>
|
||||
|
||||
#include "cli_tool_event.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t TEST_EXIT_CODE = 7;
|
||||
constexpr int64_t TEST_TIMESTAMP = 123456;
|
||||
}
|
||||
|
||||
class CliToolEventTest : public testing::Test {};
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolEvent_Parcelable_0100
|
||||
* @tc.desc: Test CliToolEvent marshalling and unmarshalling success and failure paths
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolEventTest, CliToolEvent_Parcelable_0100, TestSize.Level1)
|
||||
{
|
||||
CliToolEvent event;
|
||||
event.type = "stdout";
|
||||
event.eventData = "payload";
|
||||
event.exitCode = TEST_EXIT_CODE;
|
||||
event.timestamp = TEST_TIMESTAMP;
|
||||
|
||||
Parcel parcel;
|
||||
ASSERT_TRUE(event.Marshalling(parcel));
|
||||
parcel.RewindRead(0);
|
||||
|
||||
std::unique_ptr<CliToolEvent> unmarshalled(CliToolEvent::Unmarshalling(parcel));
|
||||
ASSERT_NE(unmarshalled, nullptr);
|
||||
EXPECT_EQ(unmarshalled->type, "stdout");
|
||||
EXPECT_EQ(unmarshalled->eventData, "payload");
|
||||
EXPECT_EQ(unmarshalled->exitCode, TEST_EXIT_CODE);
|
||||
EXPECT_EQ(unmarshalled->timestamp, TEST_TIMESTAMP);
|
||||
|
||||
Parcel partialParcel;
|
||||
ASSERT_TRUE(partialParcel.WriteString("exit"));
|
||||
partialParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliToolEvent::Unmarshalling(partialParcel), nullptr);
|
||||
|
||||
Parcel emptyParcel;
|
||||
EXPECT_EQ(CliToolEvent::Unmarshalling(emptyParcel), nullptr);
|
||||
|
||||
Parcel missingExitCodeParcel;
|
||||
ASSERT_TRUE(missingExitCodeParcel.WriteString("exit"));
|
||||
ASSERT_TRUE(missingExitCodeParcel.WriteString("payload"));
|
||||
missingExitCodeParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliToolEvent::Unmarshalling(missingExitCodeParcel), nullptr);
|
||||
|
||||
Parcel missingTimestampParcel;
|
||||
ASSERT_TRUE(missingTimestampParcel.WriteString("exit"));
|
||||
ASSERT_TRUE(missingTimestampParcel.WriteString("payload"));
|
||||
ASSERT_TRUE(missingTimestampParcel.WriteInt32(TEST_EXIT_CODE));
|
||||
missingTimestampParcel.RewindRead(0);
|
||||
EXPECT_EQ(CliToolEvent::Unmarshalling(missingTimestampParcel), nullptr);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_tool_mgr_client_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"mock/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${ability_runtime_innerkits_path}/ability_manager/include",
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_tool_mgr_client_test.cpp",
|
||||
"mock/src/mock_cli_tool_mgr_scheduler_recipient.cpp",
|
||||
"mock/src/mock_cli_tool_mgr_service.cpp",
|
||||
"mock/src/mock_system_ability_client.cpp",
|
||||
"mock/src/mock_system_ability_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_event_reply_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_mgr_load_callback.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_subscription_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_event.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_mgr_client.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_options.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_result.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_tool_param.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/sub_command_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/tool_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/tool_summary.cpp",
|
||||
]
|
||||
|
||||
cflags = []
|
||||
if (target_cpu == "arm") {
|
||||
cflags += [ "-BINDER_IPC_32BIT" ]
|
||||
}
|
||||
|
||||
deps = []
|
||||
|
||||
external_deps = [
|
||||
"ability_base:want",
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"hitrace:hitrace_meter",
|
||||
"ipc:ipc_core",
|
||||
"json:nlohmann_json_static",
|
||||
"safwk:system_ability_fwk",
|
||||
"samgr:samgr_proxy",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_tool_mgr_client_test" ]
|
||||
}
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define private public
|
||||
#include "cli_event_reply_manager.h"
|
||||
#include "cli_session_subscription_manager.h"
|
||||
#include "cli_tool_mgr_client.h"
|
||||
#undef private
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "mock_cli_tool_mgr_client_flag.h"
|
||||
#include "mock_cli_tool_mgr_service.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
ToolInfo BuildToolInfo(const std::string &name)
|
||||
{
|
||||
ToolInfo tool;
|
||||
tool.name = name;
|
||||
tool.version = "1.0.0";
|
||||
tool.description = "mock tool";
|
||||
tool.executablePath = "/system/bin/mock";
|
||||
tool.inputSchema = "{}";
|
||||
tool.outputSchema = "{}";
|
||||
return tool;
|
||||
}
|
||||
|
||||
ToolSummary BuildToolSummary(const std::string &name)
|
||||
{
|
||||
ToolSummary summary;
|
||||
summary.name = name;
|
||||
summary.version = "1.0.0";
|
||||
summary.description = "mock summary";
|
||||
return summary;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class MockSessionCallback : public SessionEventCallback {
|
||||
public:
|
||||
void OnToolEvent(const std::string &, const std::string &, const CliToolEvent &event) override
|
||||
{
|
||||
eventCount++;
|
||||
lastEventType = event.type;
|
||||
}
|
||||
|
||||
int32_t eventCount = 0;
|
||||
std::string lastEventType;
|
||||
};
|
||||
|
||||
class CliToolMGRClientTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
CliToolMgrClientFlag::Reset();
|
||||
CliEventReplyManager::GetInstance().ClearAllEvent();
|
||||
CliSessionSubscriptionManager::GetInstance().ClearAllSubscriptions();
|
||||
auto &client = CliToolMGRClient::GetInstance();
|
||||
client.ClearProxy();
|
||||
client.loadSaFinished_ = false;
|
||||
client.serviceDeathHandlers_.clear();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
auto &client = CliToolMGRClient::GetInstance();
|
||||
client.ClearProxy();
|
||||
CliToolMgrClientFlag::Reset();
|
||||
}
|
||||
|
||||
sptr<MockCliToolMgrService> SetMockService()
|
||||
{
|
||||
auto mockService = sptr<MockCliToolMgrService>::MakeSptr();
|
||||
CliToolMgrClientFlag::cliToolMgr = mockService->AsObject();
|
||||
CliToolMGRClient::GetInstance().cliToolMgr_ = mockService;
|
||||
return mockService;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @tc.name: GetInstance_0100
|
||||
* @tc.desc: Test GetInstance returns singleton instance
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, GetInstance_0100, TestSize.Level1)
|
||||
{
|
||||
auto &instance1 = CliToolMGRClient::GetInstance();
|
||||
auto &instance2 = CliToolMGRClient::GetInstance();
|
||||
|
||||
EXPECT_EQ(&instance1, &instance2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: GetCliToolMgrProxy_0100
|
||||
* @tc.desc: Test cached proxy, null system ability and successful load branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, GetCliToolMgrProxy_0100, TestSize.Level1)
|
||||
{
|
||||
auto &client = CliToolMGRClient::GetInstance();
|
||||
auto mockService = SetMockService();
|
||||
EXPECT_EQ(client.GetCliToolMgrProxy()->AsObject(), mockService->AsObject());
|
||||
|
||||
client.ClearProxy();
|
||||
CliToolMgrClientFlag::nullSystemAbility = true;
|
||||
EXPECT_EQ(client.GetCliToolMgrProxy(), nullptr);
|
||||
|
||||
CliToolMgrClientFlag::nullSystemAbility = false;
|
||||
CliToolMgrClientFlag::cliToolMgr = mockService->AsObject();
|
||||
EXPECT_EQ(client.GetCliToolMgrProxy()->AsObject(), mockService->AsObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: LoadCliToolMgrService_0100
|
||||
* @tc.desc: Test load failure, timeout and success branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, LoadCliToolMgrService_0100, TestSize.Level1)
|
||||
{
|
||||
auto &client = CliToolMGRClient::GetInstance();
|
||||
CliToolMgrClientFlag::nullSystemAbility = true;
|
||||
EXPECT_FALSE(client.LoadCliToolMgrService());
|
||||
|
||||
CliToolMgrClientFlag::nullSystemAbility = false;
|
||||
CliToolMgrClientFlag::retLoadSystemAbility = ERR_INVALID_VALUE;
|
||||
EXPECT_FALSE(client.LoadCliToolMgrService());
|
||||
|
||||
CliToolMgrClientFlag::retLoadSystemAbility = ERR_OK;
|
||||
CliToolMgrClientFlag::shouldCallback = true;
|
||||
CliToolMgrClientFlag::cliToolMgr = sptr<MockCliToolMgrService>::MakeSptr()->AsObject();
|
||||
EXPECT_TRUE(client.LoadCliToolMgrService());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: QueryInterfaces_0100
|
||||
* @tc.desc: Test query/register interfaces return proxy results and populate outputs
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, QueryInterfaces_0100, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
CliToolMgrClientFlag::summaries = {BuildToolSummary("ohos-summary")};
|
||||
CliToolMgrClientFlag::toolInfos = {BuildToolInfo("ohos-tool")};
|
||||
|
||||
std::vector<ToolSummary> summaries;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolSummaries(summaries), ERR_OK);
|
||||
ASSERT_EQ(summaries.size(), 1u);
|
||||
EXPECT_EQ(summaries[0].name, "ohos-summary");
|
||||
|
||||
ToolInfo tool;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetToolInfoByName("ohos-tool", tool), ERR_OK);
|
||||
EXPECT_EQ(tool.name, "ohos-tool");
|
||||
|
||||
std::vector<ToolInfo> tools;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolInfos(tools), ERR_OK);
|
||||
ASSERT_EQ(tools.size(), 1u);
|
||||
EXPECT_EQ(tools[0].name, "ohos-tool");
|
||||
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().RegisterTool(tool), ERR_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: QueryInterfaces_0200
|
||||
* @tc.desc: Test proxy error branches for query/register interfaces
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, QueryInterfaces_0200, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
CliToolMgrClientFlag::retGetAllToolSummaries = ERR_INVALID_VALUE;
|
||||
CliToolMgrClientFlag::retGetToolInfoByName = ERR_INVALID_VALUE;
|
||||
CliToolMgrClientFlag::retGetAllToolInfos = ERR_INVALID_VALUE;
|
||||
CliToolMgrClientFlag::retRegisterTool = ERR_INVALID_VALUE;
|
||||
|
||||
std::vector<ToolSummary> summaries;
|
||||
ToolInfo tool;
|
||||
std::vector<ToolInfo> tools;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolSummaries(summaries), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetToolInfoByName("ohos-tool", tool), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolInfos(tools), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().RegisterTool(tool), ERR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: NullProxyInterfaces_0100
|
||||
* @tc.desc: Test public interfaces return service-connect failure when proxy cannot be loaded
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, NullProxyInterfaces_0100, TestSize.Level1)
|
||||
{
|
||||
CliToolMgrClientFlag::nullSystemAbility = true;
|
||||
std::vector<ToolSummary> summaries;
|
||||
ToolInfo tool;
|
||||
std::vector<ToolInfo> tools;
|
||||
std::vector<Command> commands;
|
||||
std::vector<CommandPermission> permissions;
|
||||
CliSessionInfo session;
|
||||
std::string subscriptionId;
|
||||
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolSummaries(summaries), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetToolInfoByName("tool", tool), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().GetAllToolInfos(tools), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().RegisterTool(tool), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ExecTool(ExecToolParam {}, nullptr), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SubscribeSession("session", std::make_shared<MockSessionCallback>(),
|
||||
subscriptionId), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().UnsubscribeSession("session", "sub"), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ClearSession("session"), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().QuerySession("session", session), GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SendMessage("session", "input", nullptr),
|
||||
GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().BatchQueryPermissionBySubCommand(commands, permissions),
|
||||
GET_CLI_TOOL_MGR_SERVICE_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: ExecTool_0100
|
||||
* @tc.desc: Test scheduler failure, execute failure cleanup and success callback activation
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, ExecTool_0100, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
ExecToolParam param;
|
||||
param.toolName = "ohos-tool";
|
||||
int32_t callbackCode = -1;
|
||||
|
||||
CliToolMgrClientFlag::retRegisterScheduler = ERR_INVALID_VALUE;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ExecTool(param,
|
||||
[&callbackCode](int32_t code, const CliSessionInfo &) { callbackCode = code; }), ERR_INVALID_VALUE);
|
||||
|
||||
CliToolMGRClient::GetInstance().schedulerRegistered_ = false;
|
||||
CliToolMgrClientFlag::retRegisterScheduler = ERR_OK;
|
||||
CliToolMgrClientFlag::retExecTool = ERR_INVALID_VALUE;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ExecTool(param,
|
||||
[&callbackCode](int32_t code, const CliSessionInfo &) { callbackCode = code; }), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliEventReplyManager::GetInstance().HandleEventReply(
|
||||
CliToolMgrClientFlag::lastEventId, CliEventReplyResult {}), -1);
|
||||
|
||||
CliToolMgrClientFlag::retExecTool = ERR_OK;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ExecTool(param,
|
||||
[&callbackCode](int32_t code, const CliSessionInfo &) { callbackCode = code; }), ERR_OK);
|
||||
CliSessionInfo session;
|
||||
session.sessionId = "session";
|
||||
CliEventReplyResult result;
|
||||
result.code = ERR_OK;
|
||||
result.sessionInfo = session;
|
||||
EXPECT_EQ(CliEventReplyManager::GetInstance().HandleEventReply(CliToolMgrClientFlag::lastEventId, result), ERR_OK);
|
||||
EXPECT_EQ(callbackCode, ERR_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: SessionInterfaces_0100
|
||||
* @tc.desc: Test subscribe/unsubscribe/query/clear interfaces
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, SessionInterfaces_0100, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
std::string subscriptionId;
|
||||
auto callback = std::make_shared<MockSessionCallback>();
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SubscribeSession("session", callback, subscriptionId), ERR_OK);
|
||||
EXPECT_FALSE(subscriptionId.empty());
|
||||
|
||||
CliToolEvent event;
|
||||
event.type = "stdout";
|
||||
EXPECT_EQ(CliSessionSubscriptionManager::GetInstance().HandleSessionEvent("session", subscriptionId, event),
|
||||
ERR_OK);
|
||||
EXPECT_EQ(callback->eventCount, 1);
|
||||
EXPECT_EQ(callback->lastEventType, "stdout");
|
||||
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().UnsubscribeSession("session", subscriptionId), ERR_OK);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ClearSession("session"), ERR_OK);
|
||||
|
||||
CliToolMgrClientFlag::querySession.sessionId = "session";
|
||||
CliSessionInfo session;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().QuerySession("session", session), ERR_OK);
|
||||
EXPECT_EQ(session.sessionId, "session");
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: SessionInterfaces_0200
|
||||
* @tc.desc: Test subscribe failure removes provisional subscription and direct session proxy errors
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, SessionInterfaces_0200, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
CliToolMgrClientFlag::retSubscribeSession = ERR_INVALID_VALUE;
|
||||
std::string subscriptionId;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SubscribeSession(
|
||||
"session", std::make_shared<MockSessionCallback>(), subscriptionId), ERR_INVALID_VALUE);
|
||||
EXPECT_TRUE(subscriptionId.empty());
|
||||
|
||||
CliToolMgrClientFlag::retUnsubscribeSession = ERR_INVALID_VALUE;
|
||||
CliToolMgrClientFlag::retClearSession = ERR_INVALID_VALUE;
|
||||
CliToolMgrClientFlag::retQuerySession = ERR_INVALID_VALUE;
|
||||
CliSessionInfo session;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().UnsubscribeSession("session", "sub"), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().ClearSession("session"), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().QuerySession("session", session), ERR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: SendMessage_0100
|
||||
* @tc.desc: Test send message failure cleanup and success callback activation
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, SendMessage_0100, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
int32_t callbackCode = -1;
|
||||
CliToolMgrClientFlag::retSendMessage = ERR_INVALID_VALUE;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SendMessage(
|
||||
"session", "input", [&callbackCode](int32_t code) { callbackCode = code; }), ERR_INVALID_VALUE);
|
||||
EXPECT_EQ(CliEventReplyManager::GetInstance().HandleEventReply(
|
||||
CliToolMgrClientFlag::lastEventId, CliEventReplyResult {}), -1);
|
||||
|
||||
CliToolMgrClientFlag::retSendMessage = ERR_OK;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().SendMessage(
|
||||
"session", "input", [&callbackCode](int32_t code) { callbackCode = code; }), ERR_OK);
|
||||
CliEventReplyResult result;
|
||||
result.code = ERR_OK;
|
||||
EXPECT_EQ(CliEventReplyManager::GetInstance().HandleEventReply(CliToolMgrClientFlag::lastEventId, result), ERR_OK);
|
||||
EXPECT_EQ(callbackCode, ERR_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: BatchQueryPermission_0100
|
||||
* @tc.desc: Test batch query permission success and failure forwarding
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, BatchQueryPermission_0100, TestSize.Level1)
|
||||
{
|
||||
SetMockService();
|
||||
CommandPermission permission;
|
||||
permission.cmd.toolName = "ohos-tool";
|
||||
permission.permissions = {"ohos.permission.TEST"};
|
||||
CliToolMgrClientFlag::commandPermissions = {permission};
|
||||
std::vector<Command> commands = {Command {"ohos-tool", ""}};
|
||||
std::vector<CommandPermission> permissions;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().BatchQueryPermissionBySubCommand(commands, permissions), ERR_OK);
|
||||
ASSERT_EQ(permissions.size(), 1u);
|
||||
EXPECT_EQ(permissions[0].permissions[0], "ohos.permission.TEST");
|
||||
|
||||
CliToolMgrClientFlag::retBatchQueryPermission = ERR_INVALID_VALUE;
|
||||
EXPECT_EQ(CliToolMGRClient::GetInstance().BatchQueryPermissionBySubCommand(commands, permissions),
|
||||
ERR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: ProxyLifecycle_0100
|
||||
* @tc.desc: Test callbacks, clear proxy and death recipient branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMGRClientTest, ProxyLifecycle_0100, TestSize.Level1)
|
||||
{
|
||||
auto &client = CliToolMGRClient::GetInstance();
|
||||
auto mockService = SetMockService();
|
||||
client.schedulerRegistered_ = true;
|
||||
bool deathHandlerCalled = false;
|
||||
client.serviceDeathHandlers_.push_back([&deathHandlerCalled]() { deathHandlerCalled = true; });
|
||||
client.ClearProxy();
|
||||
EXPECT_EQ(client.cliToolMgr_, nullptr);
|
||||
EXPECT_FALSE(client.schedulerRegistered_);
|
||||
EXPECT_TRUE(deathHandlerCalled);
|
||||
|
||||
client.OnLoadSystemAbilitySuccess(mockService->AsObject());
|
||||
EXPECT_NE(client.cliToolMgr_, nullptr);
|
||||
EXPECT_TRUE(client.loadSaFinished_);
|
||||
|
||||
client.OnLoadSystemAbilityFail();
|
||||
EXPECT_EQ(client.cliToolMgr_, nullptr);
|
||||
EXPECT_TRUE(client.loadSaFinished_);
|
||||
|
||||
bool recipientCalled = false;
|
||||
CliToolMGRClient::CliMgrDeathRecipient recipient(
|
||||
[&recipientCalled](const wptr<IRemoteObject> &) { recipientCalled = true; });
|
||||
recipient.OnRemoteDied(nullptr);
|
||||
EXPECT_TRUE(recipientCalled);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SCHEDULER_RECIPIENT_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SCHEDULER_RECIPIENT_H
|
||||
|
||||
#include "icli_tool_manager_scheduler.h"
|
||||
#include "iremote_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class CliToolManagerSchedulerRecipient : public IRemoteStub<ICliToolManagerScheduler> {
|
||||
public:
|
||||
int32_t SchedulerSessionEvent(
|
||||
const std::string &sessionId, const std::string &subscriptionId, const CliToolEvent &event) override;
|
||||
int32_t SchedulerInputReplyEvent(const std::string &eventId, int32_t resultCode) override;
|
||||
int32_t SchedulerExecToolReplyEvent(
|
||||
const std::string &eventId, int32_t resultCode, const CliSessionInfo &session) override;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SCHEDULER_RECIPIENT_H
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_DATA_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_DATA_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
struct Command {
|
||||
std::string toolName;
|
||||
std::string subCommand;
|
||||
};
|
||||
|
||||
struct CommandPermission {
|
||||
Command cmd;
|
||||
std::vector<std::string> permissions;
|
||||
int32_t queryRet = 0;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_DATA_H
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_H
|
||||
|
||||
#include "cli_session_info.h"
|
||||
#include "exec_tool_param.h"
|
||||
#include "icli_tool_data.h"
|
||||
#include "icli_tool_manager_scheduler.h"
|
||||
#include "iremote_broker.h"
|
||||
#include "tool_info.h"
|
||||
#include "tool_summary.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class ICliToolManager : public IRemoteBroker {
|
||||
public:
|
||||
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.CliTool.ICliToolManager")
|
||||
|
||||
virtual int32_t GetAllToolSummaries(std::vector<ToolSummary> &summaries) = 0;
|
||||
virtual int32_t GetToolInfoByName(const std::string &name, ToolInfo &tool) = 0;
|
||||
virtual int32_t GetAllToolInfos(ToolsRawData &tools) = 0;
|
||||
virtual int32_t RegisterTool(const ToolInfo &tool) = 0;
|
||||
virtual int32_t ExecTool(const ExecToolParam ¶m, const std::string &eventId) = 0;
|
||||
virtual int32_t SubscribeSession(const std::string &sessionId, const std::string &subscriptionId) = 0;
|
||||
virtual int32_t UnsubscribeSession(const std::string &sessionId, const std::string &subscriptionId) = 0;
|
||||
virtual int32_t ClearSession(const std::string &sessionId) = 0;
|
||||
virtual int32_t QuerySession(const std::string &sessionId, CliSessionInfo &session) = 0;
|
||||
virtual int32_t SendMessage(const std::string &sessionId, const std::string &inputText,
|
||||
const std::string &eventId) = 0;
|
||||
virtual int32_t RegisterScheduler(const sptr<ICliToolManagerScheduler> &scheduler) = 0;
|
||||
virtual int32_t UnregisterScheduler() = 0;
|
||||
virtual int32_t BatchQueryPermissionBySubCommand(
|
||||
const std::vector<Command> &cmds, std::vector<CommandPermission> &cmdPermissions) = 0;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_H
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_SCHEDULER_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_SCHEDULER_H
|
||||
|
||||
#include "cli_session_info.h"
|
||||
#include "cli_tool_event.h"
|
||||
#include "iremote_broker.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class ICliToolManagerScheduler : public IRemoteBroker {
|
||||
public:
|
||||
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.CliTool.ICliToolManagerScheduler")
|
||||
|
||||
virtual int32_t SchedulerSessionEvent(
|
||||
const std::string &sessionId, const std::string &subscriptionId, const CliToolEvent &event) = 0;
|
||||
virtual int32_t SchedulerInputReplyEvent(const std::string &eventId, int32_t resultCode) = 0;
|
||||
virtual int32_t SchedulerExecToolReplyEvent(
|
||||
const std::string &eventId, int32_t resultCode, const CliSessionInfo &session) = 0;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_SCHEDULER_H
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_IF_SYSTEM_ABILITY_MANAGER_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_IF_SYSTEM_ABILITY_MANAGER_H
|
||||
|
||||
#include "iremote_broker.h"
|
||||
#include "system_ability_load_callback_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
class ISystemAbilityManager : public IRemoteBroker {
|
||||
public:
|
||||
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.ISystemAbilityManager")
|
||||
virtual int32_t LoadSystemAbility(int32_t systemAbilityId, const sptr<ISystemAbilityLoadCallback> &callback) = 0;
|
||||
};
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_IF_SYSTEM_ABILITY_MANAGER_H
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_ISERVICE_REGISTRY_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_ISERVICE_REGISTRY_H
|
||||
|
||||
#include "if_system_ability_manager.h"
|
||||
|
||||
namespace OHOS {
|
||||
class SystemAbilityManagerClient {
|
||||
public:
|
||||
static SystemAbilityManagerClient &GetInstance();
|
||||
sptr<ISystemAbilityManager> GetSystemAbilityManager();
|
||||
|
||||
private:
|
||||
SystemAbilityManagerClient() = default;
|
||||
~SystemAbilityManagerClient() = default;
|
||||
};
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_ISERVICE_REGISTRY_H
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_CLIENT_FLAG_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_CLIENT_FLAG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cli_session_info.h"
|
||||
#include "icli_tool_data.h"
|
||||
#include "iremote_object.h"
|
||||
#include "tool_info.h"
|
||||
#include "tool_summary.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class CliToolMgrClientFlag {
|
||||
public:
|
||||
static int32_t retGetAllToolSummaries;
|
||||
static int32_t retGetToolInfoByName;
|
||||
static int32_t retGetAllToolInfos;
|
||||
static int32_t retRegisterTool;
|
||||
static int32_t retExecTool;
|
||||
static int32_t retSubscribeSession;
|
||||
static int32_t retUnsubscribeSession;
|
||||
static int32_t retClearSession;
|
||||
static int32_t retQuerySession;
|
||||
static int32_t retSendMessage;
|
||||
static int32_t retRegisterScheduler;
|
||||
static int32_t retBatchQueryPermission;
|
||||
static int32_t retLoadSystemAbility;
|
||||
static bool nullSystemAbility;
|
||||
static bool shouldCallback;
|
||||
static sptr<IRemoteObject> cliToolMgr;
|
||||
static std::string lastEventId;
|
||||
static std::string lastSubscriptionId;
|
||||
static std::vector<ToolInfo> toolInfos;
|
||||
static std::vector<ToolSummary> summaries;
|
||||
static CliSessionInfo querySession;
|
||||
static std::vector<CommandPermission> commandPermissions;
|
||||
|
||||
static void Reset();
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_CLIENT_FLAG_H
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SERVICE_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SERVICE_H
|
||||
|
||||
#include "icli_tool_manager.h"
|
||||
#include "iremote_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class MockCliToolMgrService : public IRemoteStub<ICliToolManager> {
|
||||
public:
|
||||
int32_t GetAllToolSummaries(std::vector<ToolSummary> &summaries) override;
|
||||
int32_t GetToolInfoByName(const std::string &name, ToolInfo &tool) override;
|
||||
int32_t GetAllToolInfos(ToolsRawData &tools) override;
|
||||
int32_t RegisterTool(const ToolInfo &tool) override;
|
||||
int32_t ExecTool(const ExecToolParam ¶m, const std::string &eventId) override;
|
||||
int32_t SubscribeSession(const std::string &sessionId, const std::string &subscriptionId) override;
|
||||
int32_t UnsubscribeSession(const std::string &sessionId, const std::string &subscriptionId) override;
|
||||
int32_t ClearSession(const std::string &sessionId) override;
|
||||
int32_t QuerySession(const std::string &sessionId, CliSessionInfo &session) override;
|
||||
int32_t SendMessage(const std::string &sessionId, const std::string &inputText,
|
||||
const std::string &eventId) override;
|
||||
int32_t RegisterScheduler(const sptr<ICliToolManagerScheduler> &scheduler) override;
|
||||
int32_t UnregisterScheduler() override;
|
||||
int32_t BatchQueryPermissionBySubCommand(
|
||||
const std::vector<Command> &cmds, std::vector<CommandPermission> &cmdPermissions) override;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SERVICE_H
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_MOCK_CLI_SYSTEM_ABILITY_MANAGER_H
|
||||
#define OHOS_ABILITY_RUNTIME_MOCK_CLI_SYSTEM_ABILITY_MANAGER_H
|
||||
|
||||
#include "if_system_ability_manager.h"
|
||||
#include "iremote_stub.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class MockSystemAbilityManager : public IRemoteStub<ISystemAbilityManager> {
|
||||
public:
|
||||
int32_t LoadSystemAbility(int32_t systemAbilityId, const sptr<ISystemAbilityLoadCallback> &callback) override;
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_SYSTEM_ABILITY_MANAGER_H
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#include "cli_tool_mgr_scheduler_recipient.h"
|
||||
|
||||
#include "cli_event_reply_manager.h"
|
||||
#include "cli_session_subscription_manager.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t CliToolManagerSchedulerRecipient::SchedulerSessionEvent(
|
||||
const std::string &sessionId, const std::string &subscriptionId, const CliToolEvent &event)
|
||||
{
|
||||
return CliSessionSubscriptionManager::GetInstance().HandleSessionEvent(sessionId, subscriptionId, event);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerSchedulerRecipient::SchedulerInputReplyEvent(const std::string &eventId, int32_t resultCode)
|
||||
{
|
||||
CliEventReplyResult result;
|
||||
result.code = resultCode;
|
||||
return CliEventReplyManager::GetInstance().HandleEventReply(eventId, result);
|
||||
}
|
||||
|
||||
int32_t CliToolManagerSchedulerRecipient::SchedulerExecToolReplyEvent(
|
||||
const std::string &eventId, int32_t resultCode, const CliSessionInfo &session)
|
||||
{
|
||||
CliEventReplyResult result;
|
||||
result.code = resultCode;
|
||||
result.sessionInfo = session;
|
||||
return CliEventReplyManager::GetInstance().HandleEventReply(eventId, result);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#include "mock_cli_tool_mgr_service.h"
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "mock_cli_tool_mgr_client_flag.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t CliToolMgrClientFlag::retGetAllToolSummaries = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retGetToolInfoByName = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retGetAllToolInfos = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retRegisterTool = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retExecTool = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retSubscribeSession = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retUnsubscribeSession = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retClearSession = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retQuerySession = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retSendMessage = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retRegisterScheduler = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retBatchQueryPermission = ERR_OK;
|
||||
int32_t CliToolMgrClientFlag::retLoadSystemAbility = ERR_OK;
|
||||
bool CliToolMgrClientFlag::nullSystemAbility = false;
|
||||
bool CliToolMgrClientFlag::shouldCallback = true;
|
||||
sptr<IRemoteObject> CliToolMgrClientFlag::cliToolMgr = nullptr;
|
||||
std::string CliToolMgrClientFlag::lastEventId;
|
||||
std::string CliToolMgrClientFlag::lastSubscriptionId;
|
||||
std::vector<ToolInfo> CliToolMgrClientFlag::toolInfos;
|
||||
std::vector<ToolSummary> CliToolMgrClientFlag::summaries;
|
||||
CliSessionInfo CliToolMgrClientFlag::querySession;
|
||||
std::vector<CommandPermission> CliToolMgrClientFlag::commandPermissions;
|
||||
|
||||
void CliToolMgrClientFlag::Reset()
|
||||
{
|
||||
retGetAllToolSummaries = ERR_OK;
|
||||
retGetToolInfoByName = ERR_OK;
|
||||
retGetAllToolInfos = ERR_OK;
|
||||
retRegisterTool = ERR_OK;
|
||||
retExecTool = ERR_OK;
|
||||
retSubscribeSession = ERR_OK;
|
||||
retUnsubscribeSession = ERR_OK;
|
||||
retClearSession = ERR_OK;
|
||||
retQuerySession = ERR_OK;
|
||||
retSendMessage = ERR_OK;
|
||||
retRegisterScheduler = ERR_OK;
|
||||
retBatchQueryPermission = ERR_OK;
|
||||
retLoadSystemAbility = ERR_OK;
|
||||
nullSystemAbility = false;
|
||||
shouldCallback = true;
|
||||
cliToolMgr = nullptr;
|
||||
lastEventId.clear();
|
||||
lastSubscriptionId.clear();
|
||||
toolInfos.clear();
|
||||
summaries.clear();
|
||||
querySession = {};
|
||||
commandPermissions.clear();
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::GetAllToolSummaries(std::vector<ToolSummary> &summaries)
|
||||
{
|
||||
summaries = CliToolMgrClientFlag::summaries;
|
||||
return CliToolMgrClientFlag::retGetAllToolSummaries;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::GetToolInfoByName(const std::string &, ToolInfo &tool)
|
||||
{
|
||||
if (!CliToolMgrClientFlag::toolInfos.empty()) {
|
||||
tool = CliToolMgrClientFlag::toolInfos.front();
|
||||
}
|
||||
return CliToolMgrClientFlag::retGetToolInfoByName;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::GetAllToolInfos(ToolsRawData &tools)
|
||||
{
|
||||
if (CliToolMgrClientFlag::retGetAllToolInfos == ERR_OK) {
|
||||
ToolsRawData::FromToolInfoVec(CliToolMgrClientFlag::toolInfos, tools);
|
||||
}
|
||||
return CliToolMgrClientFlag::retGetAllToolInfos;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::RegisterTool(const ToolInfo &)
|
||||
{
|
||||
return CliToolMgrClientFlag::retRegisterTool;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::ExecTool(const ExecToolParam &, const std::string &eventId)
|
||||
{
|
||||
CliToolMgrClientFlag::lastEventId = eventId;
|
||||
return CliToolMgrClientFlag::retExecTool;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::SubscribeSession(const std::string &, const std::string &subscriptionId)
|
||||
{
|
||||
CliToolMgrClientFlag::lastSubscriptionId = subscriptionId;
|
||||
return CliToolMgrClientFlag::retSubscribeSession;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::UnsubscribeSession(const std::string &, const std::string &)
|
||||
{
|
||||
return CliToolMgrClientFlag::retUnsubscribeSession;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::ClearSession(const std::string &)
|
||||
{
|
||||
return CliToolMgrClientFlag::retClearSession;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::QuerySession(const std::string &, CliSessionInfo &session)
|
||||
{
|
||||
session = CliToolMgrClientFlag::querySession;
|
||||
return CliToolMgrClientFlag::retQuerySession;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::SendMessage(const std::string &, const std::string &, const std::string &eventId)
|
||||
{
|
||||
CliToolMgrClientFlag::lastEventId = eventId;
|
||||
return CliToolMgrClientFlag::retSendMessage;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::RegisterScheduler(const sptr<ICliToolManagerScheduler> &)
|
||||
{
|
||||
return CliToolMgrClientFlag::retRegisterScheduler;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::UnregisterScheduler()
|
||||
{
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int32_t MockCliToolMgrService::BatchQueryPermissionBySubCommand(
|
||||
const std::vector<Command> &, std::vector<CommandPermission> &cmdPermissions)
|
||||
{
|
||||
cmdPermissions = CliToolMgrClientFlag::commandPermissions;
|
||||
return CliToolMgrClientFlag::retBatchQueryPermission;
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#include "iservice_registry.h"
|
||||
#include "mock_cli_tool_mgr_client_flag.h"
|
||||
#include "mock_system_ability_manager.h"
|
||||
|
||||
namespace OHOS {
|
||||
SystemAbilityManagerClient &SystemAbilityManagerClient::GetInstance()
|
||||
{
|
||||
static SystemAbilityManagerClient instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
sptr<ISystemAbilityManager> SystemAbilityManagerClient::GetSystemAbilityManager()
|
||||
{
|
||||
if (CliTool::CliToolMgrClientFlag::nullSystemAbility) {
|
||||
return nullptr;
|
||||
}
|
||||
return sptr<CliTool::MockSystemAbilityManager>::MakeSptr();
|
||||
}
|
||||
} // namespace OHOS
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#include "mock_system_ability_manager.h"
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "mock_cli_tool_mgr_client_flag.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
int32_t MockSystemAbilityManager::LoadSystemAbility(
|
||||
int32_t systemAbilityId, const sptr<ISystemAbilityLoadCallback> &callback)
|
||||
{
|
||||
if (CliToolMgrClientFlag::retLoadSystemAbility != ERR_OK) {
|
||||
return CliToolMgrClientFlag::retLoadSystemAbility;
|
||||
}
|
||||
if (CliToolMgrClientFlag::shouldCallback) {
|
||||
callback->OnLoadSystemAbilitySuccess(systemAbilityId, CliToolMgrClientFlag::cliToolMgr);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
import("//build/test.gni")
|
||||
import("//foundation/ability/ability_runtime/ability_runtime.gni")
|
||||
|
||||
module_output_path = "ability_runtime/ability_runtime/clitool"
|
||||
|
||||
ohos_unittest("cli_tool_mgr_scheduler_recipient_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"${ability_runtime_services_path}/common/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_tool_mgr_scheduler_recipient_test.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_event_reply_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_info.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_session_subscription_manager.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_event.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/cli_tool_mgr_scheduler_recipient.cpp",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/src/exec_result.cpp",
|
||||
]
|
||||
|
||||
deps = [ "${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client" ]
|
||||
|
||||
external_deps = [
|
||||
"ability_base:want",
|
||||
"c_utils:utils",
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"ipc:ipc_core",
|
||||
]
|
||||
}
|
||||
|
||||
group("unittest") {
|
||||
testonly = true
|
||||
deps = [ ":cli_tool_mgr_scheduler_recipient_test" ]
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <gtest/gtest.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "cli_event_reply_manager.h"
|
||||
#include "cli_session_subscription_manager.h"
|
||||
#include "cli_tool_mgr_scheduler_recipient.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
constexpr int32_t ERR_OK = 0;
|
||||
constexpr int32_t TEST_RESULT_CODE = 1001;
|
||||
}
|
||||
|
||||
class CliToolMgrSchedulerRecipientTest : public testing::Test {
|
||||
public:
|
||||
void TearDown() override
|
||||
{
|
||||
CliEventReplyManager::GetInstance().ClearAllEvent();
|
||||
CliSessionSubscriptionManager::GetInstance().ClearAllSubscriptions();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerSchedulerRecipient_0100
|
||||
* @tc.desc: Test scheduler recipient forwards events to managers
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolMgrSchedulerRecipientTest, CliToolManagerSchedulerRecipient_0100, TestSize.Level1)
|
||||
{
|
||||
CliToolManagerSchedulerRecipient recipient;
|
||||
|
||||
int32_t replyCode = 0;
|
||||
std::string eventId = CliEventReplyManager::GetInstance().AddEventReplyCallback("reply-",
|
||||
[&](const CliEventReplyResult &result) {
|
||||
replyCode = result.code;
|
||||
});
|
||||
CliEventReplyManager::GetInstance().ActivateEventReplyCallback(eventId);
|
||||
EXPECT_EQ(recipient.SchedulerInputReplyEvent(eventId, TEST_RESULT_CODE), ERR_OK);
|
||||
EXPECT_EQ(replyCode, TEST_RESULT_CODE);
|
||||
|
||||
std::optional<CliSessionInfo> replySession;
|
||||
std::string execEventId = CliEventReplyManager::GetInstance().AddEventReplyCallback("exec-",
|
||||
[&](const CliEventReplyResult &result) {
|
||||
replySession = result.sessionInfo;
|
||||
});
|
||||
CliEventReplyManager::GetInstance().ActivateEventReplyCallback(execEventId);
|
||||
CliSessionInfo session;
|
||||
session.sessionId = "scheduler-session";
|
||||
EXPECT_EQ(recipient.SchedulerExecToolReplyEvent(execEventId, ERR_OK, session), ERR_OK);
|
||||
ASSERT_TRUE(replySession.has_value());
|
||||
EXPECT_EQ(replySession->sessionId, "scheduler-session");
|
||||
|
||||
int32_t sessionEventCount = 0;
|
||||
CliToolEvent event;
|
||||
event.type = "stdout";
|
||||
std::string subscriptionId = CliSessionSubscriptionManager::GetInstance().AddProvisionalSubscription("session",
|
||||
[&](const std::string &, const std::string &, const CliToolEvent &) {
|
||||
sessionEventCount++;
|
||||
});
|
||||
CliSessionSubscriptionManager::GetInstance().ActivateSubscription(subscriptionId);
|
||||
EXPECT_EQ(recipient.SchedulerSessionEvent("session", subscriptionId, event), ERR_OK);
|
||||
EXPECT_EQ(sessionEventCount, 1);
|
||||
}
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
+19
-7
@@ -20,35 +20,46 @@ ohos_unittest("cli_tool_mgr_service_test") {
|
||||
module_out_path = module_output_path
|
||||
|
||||
include_dirs = [
|
||||
"${ability_runtime_innerkits_path}/app_manager/include/appmgr",
|
||||
"${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper",
|
||||
"${cli_tool_framework_path}/services/climgr/include",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${ability_runtime_path}/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool/include",
|
||||
"${cli_tool_framework_path}/services/climgr/include",
|
||||
"${cli_tool_framework_path}/services/common/include",
|
||||
"${cli_tool_framework_path}/test/unittest/common_mock/climgr_data/include",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"cli_tool_mgr_service_test.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/cli_tool_app_state_observer.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/cli_tool_manager_service.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/cli_tool_data_manager.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/event_dispatcher.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/io_monitor.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/permission_query_util.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/process_manager.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/session_record.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/tool_util.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/event_dispatcher.cpp",
|
||||
"${cli_tool_framework_path}/services/climgr/src/io_monitor.cpp",
|
||||
"${cli_tool_framework_path}/services/common/src/ccm_util.cpp",
|
||||
"${cli_tool_framework_path}/services/common/src/permission_util.cpp",
|
||||
"${cli_tool_framework_path}/test/unittest/common_mock/climgr_data/src/cli_tool_data_manager_mock.cpp",
|
||||
]
|
||||
|
||||
cflags = []
|
||||
if (target_cpu == "arm") {
|
||||
cflags += [ "-BINDER_IPC_32BIT" ]
|
||||
cflags += [ "-DBINDER_IPC_32BIT" ]
|
||||
}
|
||||
|
||||
deps = [
|
||||
"${ability_runtime_innerkits_path}/ability_manager:ability_manager",
|
||||
"${ability_runtime_innerkits_path}/app_manager:app_manager",
|
||||
"${ability_runtime_native_path}/appkit:appkit_manager_helper",
|
||||
"${cli_tool_framework_path}/interfaces/cli_tool:cli_tool_client",
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"access_token:libaccesstoken_sdk",
|
||||
"access_token:libnativetoken",
|
||||
"access_token:libtoken_setproc",
|
||||
"access_token:libtokenid_sdk",
|
||||
"bundle_framework:appexecfwk_base",
|
||||
"bundle_framework:appexecfwk_core",
|
||||
@@ -57,11 +68,12 @@ ohos_unittest("cli_tool_mgr_service_test") {
|
||||
"googletest:gmock_main",
|
||||
"googletest:gtest_main",
|
||||
"hilog:libhilog",
|
||||
"init:libbegetutil",
|
||||
"ipc:ipc_core",
|
||||
"json:nlohmann_json_static",
|
||||
"kv_store:distributeddata_inner",
|
||||
"samgr:samgr_proxy",
|
||||
"safwk:system_ability_fwk",
|
||||
"samgr:samgr_proxy",
|
||||
]
|
||||
}
|
||||
|
||||
+914
@@ -0,0 +1,914 @@
|
||||
/*
|
||||
* Copyright (c) 2026 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 <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#define protected public
|
||||
#define private public
|
||||
#include "cli_tool_manager_service.h"
|
||||
#undef private
|
||||
#undef protected
|
||||
|
||||
#include "cli_error_code.h"
|
||||
#include "cli_tool_app_state_observer.h"
|
||||
#include "ccm_util.h"
|
||||
#include "event_dispatcher.h"
|
||||
#include "exec_options.h"
|
||||
#include "nativetoken_kit.h"
|
||||
#include "token_setproc.h"
|
||||
#include "tool_info.h"
|
||||
#include "tool_util.h"
|
||||
|
||||
using namespace testing::ext;
|
||||
using namespace OHOS::CliTool;
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
namespace {
|
||||
const char *CLI_TOOL_PERMS[] = {
|
||||
"ohos.permission.EXEC_CLI_TOOL",
|
||||
};
|
||||
|
||||
bool IsPermissionGateResult(int32_t result)
|
||||
{
|
||||
return result == ERR_NOT_SYSTEM_APP || result == ERR_PERMISSION_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
class CliToolManagerServiceTest : public testing::Test {
|
||||
public:
|
||||
static void SetUpTestCase(void);
|
||||
static void TearDownTestCase(void);
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
void RegisterTestTool(const std::string& name, const std::string& schema);
|
||||
|
||||
sptr<CliToolManagerService> service_;
|
||||
};
|
||||
|
||||
void CliToolManagerServiceTest::SetUpTestCase(void)
|
||||
{
|
||||
NativeTokenInfoParams infoInstance = {
|
||||
.dcapsNum = 0,
|
||||
.permsNum = static_cast<int32_t>(sizeof(CLI_TOOL_PERMS) / sizeof(CLI_TOOL_PERMS[0])),
|
||||
.aclsNum = 0,
|
||||
.dcaps = nullptr,
|
||||
.perms = CLI_TOOL_PERMS,
|
||||
.acls = nullptr,
|
||||
.aplStr = "system_core",
|
||||
};
|
||||
infoInstance.processName = "CliToolManagerServiceTest";
|
||||
auto tokenId = GetAccessTokenId(&infoInstance);
|
||||
SetSelfTokenID(tokenId);
|
||||
}
|
||||
|
||||
void CliToolManagerServiceTest::TearDownTestCase(void)
|
||||
{
|
||||
// Cleanup test environment
|
||||
}
|
||||
|
||||
void CliToolManagerServiceTest::SetUp()
|
||||
{
|
||||
service_ = CliToolManagerService::GetInstance();
|
||||
service_->interfaceCalledCount_.store(0);
|
||||
EventDispatcher::GetInstance().ClearAll();
|
||||
std::lock_guard<ffrt::mutex> guard(service_->sessionsMutex_);
|
||||
service_->sessionRecords_.clear();
|
||||
}
|
||||
|
||||
void CliToolManagerServiceTest::TearDown()
|
||||
{
|
||||
service_->interfaceCalledCount_.store(0);
|
||||
EventDispatcher::GetInstance().ClearAll();
|
||||
std::lock_guard<ffrt::mutex> guard(service_->sessionsMutex_);
|
||||
service_->sessionRecords_.clear();
|
||||
}
|
||||
|
||||
void CliToolManagerServiceTest::RegisterTestTool(const std::string& name, const std::string& schema)
|
||||
{
|
||||
ToolInfo tool;
|
||||
tool.name = name;
|
||||
tool.description = "Test tool: " + name;
|
||||
tool.executablePath = "/system/bin/" + name;
|
||||
tool.inputSchema = schema;
|
||||
CliToolDataManager::GetInstance().RegisterTool(tool);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_GetInstance_0100
|
||||
* @tc.desc: Test GetInstance returns singleton instance
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, GetInstance_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetInstance_0100 start";
|
||||
|
||||
auto instance1 = CliToolManagerService::GetInstance();
|
||||
auto instance2 = CliToolManagerService::GetInstance();
|
||||
|
||||
EXPECT_EQ(instance1.GetRefPtr(), instance2.GetRefPtr());
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetInstance_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_OnIdle_0100
|
||||
* @tc.desc: Test OnIdle blocks unload when IPC or session is active
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, OnIdle_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_OnIdle_0100 start";
|
||||
|
||||
SystemAbilityOnDemandReason idleReason;
|
||||
|
||||
EXPECT_EQ(service_->OnIdle(idleReason), 0);
|
||||
|
||||
service_->interfaceCalledCount_.store(1);
|
||||
EXPECT_EQ(service_->OnIdle(idleReason), -1);
|
||||
|
||||
service_->interfaceCalledCount_.store(0);
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "test_session";
|
||||
service_->AddSessionRecord(record);
|
||||
EXPECT_EQ(service_->OnIdle(idleReason), -1);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_OnIdle_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_IOMonitorSendMessage_0100
|
||||
* @tc.desc: Test IOMonitor serializes high volume input writes without random pipe backpressure failure
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, IOMonitorSendMessage_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_IOMonitorSendMessage_0100 start";
|
||||
|
||||
constexpr int32_t sendCount = 1200;
|
||||
constexpr const char* sessionId = "test_session";
|
||||
const std::string message(128, 'x');
|
||||
int stdinPipe[2] = {-1, -1};
|
||||
ASSERT_EQ(pipe(stdinPipe), 0);
|
||||
|
||||
auto monitor = IOMonitor::Create();
|
||||
ASSERT_NE(monitor, nullptr);
|
||||
ASSERT_TRUE(monitor->Start());
|
||||
ASSERT_TRUE(monitor->RegisterSession(sessionId, -1, -1, stdinPipe[1]));
|
||||
|
||||
std::atomic<int32_t> replyCount = 0;
|
||||
std::atomic<int32_t> failedCount = 0;
|
||||
std::mutex replyMutex;
|
||||
std::condition_variable replyCv;
|
||||
monitor->SetInputReplyCallback([&](const std::string &, const std::string &, bool result) {
|
||||
if (!result) {
|
||||
failedCount.fetch_add(1);
|
||||
}
|
||||
if (replyCount.fetch_add(1) + 1 == sendCount) {
|
||||
std::lock_guard<std::mutex> lock(replyMutex);
|
||||
replyCv.notify_one();
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic<size_t> readBytes = 0;
|
||||
std::thread reader([&]() {
|
||||
char buffer[256] = {};
|
||||
const size_t expectedBytes = sendCount * message.size();
|
||||
while (readBytes.load() < expectedBytes) {
|
||||
ssize_t readResult = read(stdinPipe[0], buffer, sizeof(buffer));
|
||||
if (readResult > 0) {
|
||||
readBytes.fetch_add(static_cast<size_t>(readResult));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (int32_t i = 0; i < sendCount; ++i) {
|
||||
monitor->SendMessage(sessionId, message, "event_" + std::to_string(i));
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(replyMutex);
|
||||
EXPECT_TRUE(replyCv.wait_for(lock, std::chrono::seconds(5), [&]() {
|
||||
return replyCount.load() == sendCount;
|
||||
}));
|
||||
EXPECT_EQ(failedCount.load(), 0);
|
||||
|
||||
monitor->UnregisterSession(sessionId);
|
||||
monitor->Stop();
|
||||
if (reader.joinable()) {
|
||||
reader.join();
|
||||
}
|
||||
close(stdinPipe[0]);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_IOMonitorSendMessage_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_SubscribeSession_0100
|
||||
* @tc.desc: Test SubscribeSession rejects non-running sessions
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, SubscribeSession_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0100 start";
|
||||
|
||||
auto runningRecord = std::make_shared<SessionRecord>();
|
||||
runningRecord->sessionId = "running_session";
|
||||
service_->AddSessionRecord(runningRecord);
|
||||
int32_t runningRet = service_->SubscribeSession(runningRecord->sessionId, "running_subscription");
|
||||
EXPECT_TRUE(runningRet == ERR_NO_INIT || runningRet == ERR_NOT_SYSTEM_APP || runningRet == ERR_PERMISSION_DENIED);
|
||||
if (runningRet != ERR_NO_INIT) {
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0100 skipped status gate checks";
|
||||
return;
|
||||
}
|
||||
|
||||
auto completedRecord = std::make_shared<SessionRecord>();
|
||||
completedRecord->sessionId = "completed_session";
|
||||
completedRecord->SetTerminalResult(0, 0);
|
||||
completedRecord->MarkStdoutClosed();
|
||||
completedRecord->MarkStderrClosed();
|
||||
service_->AddSessionRecord(completedRecord);
|
||||
EXPECT_EQ(service_->SubscribeSession(completedRecord->sessionId, "completed_subscription"),
|
||||
ERR_CLI_SESSION_NOT_FOUND);
|
||||
|
||||
auto failedRecord = std::make_shared<SessionRecord>();
|
||||
failedRecord->sessionId = "failed_session";
|
||||
failedRecord->SetTerminalResult(1, 0);
|
||||
failedRecord->MarkStdoutClosed();
|
||||
failedRecord->MarkStderrClosed();
|
||||
service_->AddSessionRecord(failedRecord);
|
||||
EXPECT_EQ(service_->SubscribeSession(failedRecord->sessionId, "failed_subscription"), ERR_CLI_SESSION_NOT_FOUND);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ExecTool_0100
|
||||
* @tc.desc: Test ExecTool when session limit is exceeded
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ExecTool_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0100 start";
|
||||
|
||||
auto cliQuantity = CcmUtil::GetInstance().GetCliConcurrencyLimit();
|
||||
for (int32_t i = 0; i < cliQuantity; ++i) {
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "test_session_" + std::to_string(i);
|
||||
service_->AddSessionRecord(record);
|
||||
}
|
||||
|
||||
int32_t result = service_->ValidateSessionLimit();
|
||||
|
||||
EXPECT_EQ(result, ERR_SESSION_LIMIT_EXCEEDED);
|
||||
EXPECT_EQ(service_->sessionRecords_.size(), static_cast<size_t>(cliQuantity));
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ExecTool_0200
|
||||
* @tc.desc: Test ExecTool when tool does not exist
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ExecTool_0200, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0200 start";
|
||||
|
||||
ExecToolParam param;
|
||||
param.toolName = "non_existent_tool";
|
||||
param.subcommand = "";
|
||||
param.challenge = "test_challenge";
|
||||
|
||||
ToolInfo toolInfo;
|
||||
std::string sandboxConfig;
|
||||
std::string bundleName;
|
||||
int32_t result = service_->ValidateAndPrepareTool(param, 0, toolInfo, sandboxConfig, bundleName);
|
||||
|
||||
EXPECT_TRUE(result == ERR_TOOL_NOT_EXIST || result == ERR_NO_INIT);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0200 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ExecTool_0300
|
||||
* @tc.desc: Test ExecTool with empty tool name
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ExecTool_0300, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0300 start";
|
||||
|
||||
ExecToolParam param;
|
||||
param.toolName = "";
|
||||
param.subcommand = "";
|
||||
param.challenge = "test_challenge";
|
||||
|
||||
ToolInfo toolInfo;
|
||||
std::string sandboxConfig;
|
||||
std::string bundleName;
|
||||
int32_t result = service_->ValidateAndPrepareTool(param, 0, toolInfo, sandboxConfig, bundleName);
|
||||
|
||||
EXPECT_TRUE(result == ERR_TOOL_NOT_EXIST || result == ERR_NO_INIT);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0300 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ExecTool_0500
|
||||
* @tc.desc: Test ExecTool with invalid subcommand
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ExecTool_0500, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0500 start";
|
||||
|
||||
ToolInfo toolInfo;
|
||||
toolInfo.name = "test_tool_subcmd";
|
||||
toolInfo.description = "Test tool with subcommand";
|
||||
toolInfo.executablePath = "/system/bin/test_tool_subcmd";
|
||||
toolInfo.hasSubCommand = true;
|
||||
SubCommandInfo subCommandInfo;
|
||||
subCommandInfo.description = "Build subcommand";
|
||||
toolInfo.subcommands["build"] = subCommandInfo;
|
||||
|
||||
ExecToolParam param;
|
||||
param.toolName = "test_tool_subcmd";
|
||||
param.subcommand = "invalid_subcmd";
|
||||
param.challenge = "test_challenge";
|
||||
|
||||
int32_t result = ToolUtil::ValidateProperties(toolInfo, param, 0);
|
||||
|
||||
EXPECT_EQ(result, ERR_TOOL_NOT_EXIST);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0500 end";
|
||||
}
|
||||
|
||||
// ==================== Permission Validation Tests ====================
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_GetAllToolInfos_Permission_0100
|
||||
* @tc.desc: Test GetAllToolInfos permission check - should require system app and permission
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, GetAllToolInfos_Permission_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetAllToolInfos_Permission_0100 start";
|
||||
|
||||
// Note: In unit test environment, the caller is typically a system app with permissions
|
||||
// This test verifies the method completes successfully when permissions are granted
|
||||
ToolsRawData toolsRawData;
|
||||
int32_t result = service_->GetAllToolInfos(toolsRawData);
|
||||
|
||||
// In test environment, should succeed or return appropriate error
|
||||
EXPECT_TRUE(result == ERR_OK || result == ERR_NOT_SYSTEM_APP || result == ERR_PERMISSION_DENIED);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetAllToolInfos_Permission_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_GetAllToolSummaries_Permission_0100
|
||||
* @tc.desc: Test GetAllToolSummaries permission check - should require system app and permission
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, GetAllToolSummaries_Permission_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetAllToolSummaries_Permission_0100 start";
|
||||
|
||||
std::vector<ToolSummary> summaries;
|
||||
int32_t result = service_->GetAllToolSummaries(summaries);
|
||||
|
||||
// In test environment, should succeed or return appropriate error
|
||||
EXPECT_TRUE(result == ERR_OK || result == ERR_NOT_SYSTEM_APP || result == ERR_PERMISSION_DENIED);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetAllToolSummaries_Permission_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_GetToolInfoByName_Permission_0100
|
||||
* @tc.desc: Test GetToolInfoByName permission check - should require system app and permission
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, GetToolInfoByName_Permission_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetToolInfoByName_Permission_0100 start";
|
||||
|
||||
ToolInfo tool;
|
||||
int32_t result = service_->GetToolInfoByName("test_tool", tool);
|
||||
|
||||
// In test environment, should succeed or return appropriate error
|
||||
// ERR_NO_INIT (-1) indicates data manager not initialized
|
||||
EXPECT_TRUE(result == ERR_OK || result == ERR_NOT_SYSTEM_APP || result == ERR_PERMISSION_DENIED ||
|
||||
result == ERR_NO_INIT);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_GetToolInfoByName_Permission_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_QueryPermission_Required_0100
|
||||
* @tc.desc: Test that query methods require ohos.permission.QUERY_CLI_TOOL permission
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, QueryPermission_Required_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_QueryPermission_Required_0100 start";
|
||||
|
||||
// This test documents that the following methods require:
|
||||
// 1. Caller must be a system app
|
||||
// 2. Caller must have ohos.permission.QUERY_CLI_TOOL permission
|
||||
//
|
||||
// Methods covered:
|
||||
// - GetAllToolInfos
|
||||
// - GetAllToolSummaries
|
||||
// - GetToolInfoByName
|
||||
//
|
||||
// In production, if caller is not system app: returns ERR_NOT_SYSTEM_APP
|
||||
// If caller lacks permission: returns ERR_PERMISSION_DENIED
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_QueryPermission_Required_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_AppStateObserver_0100
|
||||
* @tc.desc: Test app state observer exposes a valid remote object
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, AppStateObserver_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_AppStateObserver_0100 start";
|
||||
|
||||
sptr<CliToolAppStateObserver> observer = new CliToolAppStateObserver("test.bundle", nullptr);
|
||||
|
||||
EXPECT_NE(observer->AsObject(), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_AppStateObserver_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_AppStateObserver_0200
|
||||
* @tc.desc: Test app state observer forwards process died callback
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, AppStateObserver_0200, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_AppStateObserver_0200 start";
|
||||
|
||||
std::string diedBundleName;
|
||||
pid_t diedPid = 0;
|
||||
sptr<CliToolAppStateObserver> observer = new CliToolAppStateObserver(
|
||||
"test.bundle", [&diedBundleName, &diedPid](const std::string &bundleName, pid_t pid) {
|
||||
diedBundleName = bundleName;
|
||||
diedPid = pid;
|
||||
});
|
||||
AppExecFwk::ProcessData processData;
|
||||
processData.pid = 1001;
|
||||
|
||||
observer->OnProcessDied(processData);
|
||||
|
||||
EXPECT_EQ(diedBundleName, "test.bundle");
|
||||
EXPECT_EQ(diedPid, 1001);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_AppStateObserver_0200 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_SessionRecord_0100
|
||||
* @tc.desc: Test GetSessionRecord removes null leak entries and RemoveSessionRecord erases records
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, SessionRecord_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SessionRecord_0100 start";
|
||||
|
||||
{
|
||||
std::lock_guard<ffrt::mutex> guard(service_->sessionsMutex_);
|
||||
service_->sessionRecords_["leak_session"] = nullptr;
|
||||
}
|
||||
EXPECT_EQ(service_->GetSessionRecord("leak_session"), nullptr);
|
||||
EXPECT_EQ(service_->sessionRecords_.find("leak_session"), service_->sessionRecords_.end());
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "normal_session";
|
||||
service_->AddSessionRecord(record);
|
||||
EXPECT_EQ(service_->GetSessionRecord("normal_session"), record);
|
||||
service_->RemoveSessionRecord("normal_session");
|
||||
EXPECT_EQ(service_->GetSessionRecord("normal_session"), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SessionRecord_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_CreateSessionRecord_0100
|
||||
* @tc.desc: Test CreateSessionRecord initializes session fields from ExecToolParam
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, CreateSessionRecord_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_CreateSessionRecord_0100 start";
|
||||
|
||||
ExecToolParam param;
|
||||
param.toolName = "create_tool";
|
||||
param.options.background = false;
|
||||
param.options.timeout = 12;
|
||||
|
||||
auto record = service_->CreateSessionRecord(param, "event-id");
|
||||
|
||||
ASSERT_NE(record, nullptr);
|
||||
EXPECT_EQ(record->toolName, "create_tool");
|
||||
EXPECT_TRUE(record->sessionId.find("create_tool_") == 0);
|
||||
EXPECT_EQ(record->timeoutMs, 12 * 1000);
|
||||
EXPECT_EQ(record->eventId, "event-id");
|
||||
EXPECT_EQ(record->GetState(), SessionState::RUNNING);
|
||||
EXPECT_FALSE(record->Background());
|
||||
EXPECT_GT(record->startTime, 0);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_CreateSessionRecord_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleProcessYieldTimeout_0100
|
||||
* @tc.desc: Test yield timeout missing-session and foreground-to-background branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleProcessYieldTimeout_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleProcessYieldTimeout_0100 start";
|
||||
|
||||
service_->HandleProcessYieldTimeout("missing_session");
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "yield_session";
|
||||
record->eventId = "yield_event";
|
||||
record->SetBackground(false);
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->HandleProcessYieldTimeout(record->sessionId);
|
||||
|
||||
EXPECT_TRUE(record->Background());
|
||||
EXPECT_NE(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleProcessYieldTimeout_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleProcessTimeout_0100
|
||||
* @tc.desc: Test process timeout marks CLI session timed out and cancelling
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleProcessTimeout_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleProcessTimeout_0100 start";
|
||||
|
||||
service_->HandleProcessTimeout("missing_session");
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "timeout_session";
|
||||
record->eventId = "timeout_event";
|
||||
record->processId = 999999;
|
||||
record->SetBackground(true);
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->HandleProcessTimeout(record->sessionId);
|
||||
|
||||
EXPECT_TRUE(record->TimedOut());
|
||||
EXPECT_EQ(record->GetState(), SessionState::CANCELLING);
|
||||
EXPECT_TRUE(record->Background());
|
||||
EXPECT_NE(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleProcessTimeout_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleSkillSessionTimeout_0100
|
||||
* @tc.desc: Test skill timeout removes skill session and handles missing session
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleSkillSessionTimeout_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleSkillSessionTimeout_0100 start";
|
||||
|
||||
service_->HandleSkillSessionTimeout("missing_session");
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "skill_timeout_session";
|
||||
record->eventId = "skill_timeout_event";
|
||||
record->sessionType = SessionType::SKILL;
|
||||
record->SetBackground(true);
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->HandleProcessTimeout(record->sessionId);
|
||||
|
||||
EXPECT_TRUE(record->TimedOut());
|
||||
EXPECT_EQ(record->GetState(), SessionState::FAILED);
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleSkillSessionTimeout_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleOutputClosed_0100
|
||||
* @tc.desc: Test output close branches for missing, stdout and stderr paths
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleOutputClosed_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleOutputClosed_0100 start";
|
||||
|
||||
service_->HandleOutputClosed("missing_session", true);
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "output_session";
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->HandleOutputClosed(record->sessionId, true);
|
||||
EXPECT_FALSE(record->OutputDrained());
|
||||
service_->HandleOutputClosed(record->sessionId, false);
|
||||
EXPECT_TRUE(record->OutputDrained());
|
||||
EXPECT_NE(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleOutputClosed_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_FinalizeBackgroundSession_0100
|
||||
* @tc.desc: Test finalize background session null, success and duplicate cleanup branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, FinalizeBackgroundSession_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_FinalizeBackgroundSession_0100 start";
|
||||
|
||||
service_->FinalizeBackgroundSession(nullptr);
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "finalize_session";
|
||||
record->eventId = "finalize_event";
|
||||
record->SetBackground(true);
|
||||
record->MarkStdoutClosed();
|
||||
record->MarkStderrClosed();
|
||||
record->SetTerminalResult(0, 0);
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->FinalizeBackgroundSession(record);
|
||||
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
service_->FinalizeBackgroundSession(record);
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_FinalizeBackgroundSession_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_RegisterSessionWithMonitors_0100
|
||||
* @tc.desc: Test monitor registration failure when ioMonitor is null
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, RegisterSessionWithMonitors_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterSessionWithMonitors_0100 start";
|
||||
|
||||
auto oldMonitor = service_->ioMonitor_;
|
||||
service_->ioMonitor_ = nullptr;
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "monitor_session";
|
||||
record->stdoutPipe[0] = -1;
|
||||
record->stderrPipe[0] = -1;
|
||||
record->stdinPipe[1] = -1;
|
||||
ExecToolParam param;
|
||||
|
||||
EXPECT_FALSE(service_->RegisterSessionWithMonitors(record, param));
|
||||
|
||||
service_->ioMonitor_ = oldMonitor;
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterSessionWithMonitors_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleSkillSessionComplete_0100
|
||||
* @tc.desc: Test skill completion missing, duplicate and cleanup branches
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleSkillSessionComplete_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleSkillSessionComplete_0100 start";
|
||||
|
||||
CliSessionInfo session;
|
||||
session.sessionId = "missing_skill_session";
|
||||
service_->HandleSkillSessionComplete("missing_skill_session", 0, "event", ERR_OK, session);
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "skill_complete_session";
|
||||
record->eventId = "skill_complete_event";
|
||||
record->sessionType = SessionType::SKILL;
|
||||
record->SetBackground(true);
|
||||
service_->AddSessionRecord(record);
|
||||
session.sessionId = record->sessionId;
|
||||
|
||||
service_->HandleSkillSessionComplete(record->sessionId, 0, record->eventId, ERR_OK, session);
|
||||
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
service_->HandleSkillSessionComplete(record->sessionId, 0, record->eventId, ERR_OK, session);
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleSkillSessionComplete_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_WaitPid_0100
|
||||
* @tc.desc: Test WaitPid ignores unknown pid and finalizes drained matching record
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, WaitPid_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_WaitPid_0100 start";
|
||||
|
||||
service_->WaitPid(12345, 0, 0);
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "waitpid_session";
|
||||
record->processId = 23456;
|
||||
record->MarkStdoutClosed();
|
||||
record->MarkStderrClosed();
|
||||
service_->AddSessionRecord(record);
|
||||
|
||||
service_->WaitPid(record->processId, 0, 0);
|
||||
|
||||
EXPECT_TRUE(record->HasProcessExited());
|
||||
EXPECT_EQ(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_WaitPid_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_RegisterScheduler_0100
|
||||
* @tc.desc: Test RegisterScheduler with null and valid scheduler
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, RegisterScheduler_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterScheduler_0100 start";
|
||||
|
||||
EXPECT_NE(service_->RegisterScheduler(nullptr), ERR_OK);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterScheduler_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_UnregisterScheduler_0100
|
||||
* @tc.desc: Test UnregisterScheduler clears scheduler
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, UnregisterScheduler_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_UnregisterScheduler_0100 start";
|
||||
|
||||
service_->UnregisterScheduler();
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_UnregisterScheduler_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ClearSession_0100
|
||||
* @tc.desc: Test ClearSession with missing session
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ClearSession_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ClearSession_0100 start";
|
||||
|
||||
int32_t result = service_->ClearSession("nonexistent_session");
|
||||
EXPECT_TRUE(result == ERR_CLI_SESSION_NOT_FOUND || IsPermissionGateResult(result));
|
||||
if (IsPermissionGateResult(result)) {
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ClearSession_0100 skipped session gate checks";
|
||||
return;
|
||||
}
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "completed_session";
|
||||
record->SetState(SessionState::COMPLETED);
|
||||
service_->AddSessionRecord(record);
|
||||
EXPECT_EQ(service_->ClearSession("completed_session"), ERR_CLI_SESSION_NOT_FOUND);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ClearSession_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_QuerySession_0100
|
||||
* @tc.desc: Test QuerySession with missing session returns error
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, QuerySession_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_QuerySession_0100 start";
|
||||
|
||||
CliSessionInfo session;
|
||||
int32_t result = service_->QuerySession("missing_session", session);
|
||||
EXPECT_TRUE(result == ERR_CLI_SESSION_NOT_FOUND || IsPermissionGateResult(result));
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_QuerySession_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_SubscribeSession_0200
|
||||
* @tc.desc: Test SubscribeSession with empty args and missing session
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, SubscribeSession_0200, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0200 start";
|
||||
|
||||
int32_t result = service_->SubscribeSession("", "sub1");
|
||||
EXPECT_TRUE(result == ERR_INVALID_PARAM || IsPermissionGateResult(result));
|
||||
if (IsPermissionGateResult(result)) {
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0200 skipped argument/session gate checks";
|
||||
return;
|
||||
}
|
||||
EXPECT_EQ(service_->SubscribeSession("session", ""), ERR_INVALID_PARAM);
|
||||
EXPECT_EQ(service_->SubscribeSession("missing", "sub1"), ERR_CLI_SESSION_NOT_FOUND);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_SubscribeSession_0200 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_HandleOutputDrained_0100
|
||||
* @tc.desc: Test HandleOutputDrained with missing and present sessions
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, HandleOutputDrained_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleOutputDrained_0100 start";
|
||||
|
||||
service_->HandleOutputDrained("missing_session");
|
||||
|
||||
auto record = std::make_shared<SessionRecord>();
|
||||
record->sessionId = "drained_session";
|
||||
record->processId = 12345;
|
||||
service_->AddSessionRecord(record);
|
||||
service_->HandleOutputDrained(record->sessionId);
|
||||
EXPECT_NE(service_->GetSessionRecord(record->sessionId), nullptr);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_HandleOutputDrained_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_RegisterTool_0100
|
||||
* @tc.desc: Test RegisterTool returns permission denied (system API only)
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, RegisterTool_0100, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterTool_0100 start";
|
||||
|
||||
ToolInfo tool;
|
||||
tool.name = "ohos-test";
|
||||
EXPECT_EQ(service_->RegisterTool(tool), ERR_PERMISSION_DENIED);
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_RegisterTool_0100 end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @tc.name: CliToolManagerService_ExecTool_0600
|
||||
* @tc.desc: Test ExecTool with nonexistent tool name
|
||||
* @tc.type: FUNC
|
||||
*/
|
||||
HWTEST_F(CliToolManagerServiceTest, ExecTool_0600, TestSize.Level1)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0600 start";
|
||||
|
||||
ExecToolParam param;
|
||||
param.toolName = "ohos-nonexistent_tool";
|
||||
param.options.timeout = 30;
|
||||
CliSessionInfo session;
|
||||
int32_t result = service_->ExecTool(param, "event_exec_0600");
|
||||
EXPECT_TRUE(result == ERR_TOOL_NOT_EXIST || IsPermissionGateResult(result));
|
||||
|
||||
GTEST_LOG_(INFO) << "CliToolManagerService_ExecTool_0600 end";
|
||||
}
|
||||
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
|
||||
#ifndef OHOS_ABILITY_RUNTIME_CCM_UTIL_H
|
||||
#define OHOS_ABILITY_RUNTIME_CCM_UTIL_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace OHOS {
|
||||
namespace CliTool {
|
||||
class CcmUtil {
|
||||
public:
|
||||
static CcmUtil &GetInstance();
|
||||
int32_t GetCliConcurrencyLimit();
|
||||
};
|
||||
} // namespace CliTool
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // OHOS_ABILITY_RUNTIME_CCM_UTIL_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user